Servo Plotter




Introduction: Servo Plotter

About: 55+ years in electronics, computers, and teaching ... now retired.

This instructable explains how to make an A4 plotter using three servo motors and an Arduino UNO R3.

Construction is simple ... all you require is a hacksaw, a sharp knife, and a 3mm drill.

Two plotters are described:

  • The first version, shown in photo 2, uses MG 996R metal gear servos.
  • A second lightweight version using SG90 servos is shown in photo 5

Both plotters use the same software which features an onboard g-code interpreter that is Inkscape compatible. [1]

The MG 996R plotter features a vacuum hold-down and a simple pen-lift. The reason that the sample plot in photo 6 looks weird is that servo1 is faulty ... I have some replacements on order. [2]

While waiting for the replacement servos to arrive I built a lightweight plotter from two SG90 servos to verify the inverse kinematics discussed further on. The sample plot for the SG90 plotter using these equations is shown in photo 7.

The plotters work ... but not as well as my digital versions.

I am publishing my results in the hope that the construction details and solutions to issues that I have encountered are of help to others.

The estimated cost for the MG 996 plotter is less than $100.


  • the first video shows the SG90 plotter in operation
  • the second video shows the RDS3225 plotter in operation[3]
  • photo 1 is the cover image
  • photo 2 shows the assembled plotter
  • photo 3 shows the plotter dissassembled.
  • photo 4 shows the electronic components.
  • photo 5 shows an SG90 plotter made to the same dimensions
  • photo 6 shows a sample plot from the MG 996R plotter ... unfortunately servo1 is faulty.
  • photo 7 shows a sample plot from the SG90 plotter. The code is the same for both plotters.

Warning : Keep clear of the servo arms when using this plotter as the motors are extremely powerful and capable of rapid movement.



The software for this plotter has been adapted from the following instructables:


For a servo plotter to work, each motor must resist the torque from the opposite motor.

An ideal servo should hold its position if it experiences an external torque ... in practice there is a slight movement or “dead-band” until the servo resists.

The dead-band for each my MG 996R servos is shown in photo 6. To obtain these curves I simply pressed against each servo arm until I felt resistance and the servo started to vibrate.

Ideally we should only see a very small trace which is not the case for servo1


14 December 2020

  • The faulty MG 996 servos have been replaced with RDS 3225 servos.
  • See Step 9: Addenda ... for photos and alternate software

Step 1: Parts List

The following parts were obtained from

  • 1 only Arduino UNO R3 with USB cable
  • 2 only MG996R metal gear servos (optionally RDS3225 ... see Step 9)
  • 2 only 25T (tooth) metal disc stents for MG996R servos
  • 1 only Tower Pro SG90 servo
  • 1 only Prototype PCB Expansion Board For Arduino ATMEGA328P UNO R3 Shield FR-4 Fiber PCB Breadboard 2mm 2.54mm Pitch
  • CPS-3205 II 160 w (110Vac / 220Vac) 0-32 v / 0-5A, compact digitally adjustable DC power supply

The following parts were obtained locally:

  • 1 only plastic dual suction_cup (photo 1)
  • 1 meter 25mm x 1mm aluminium extrusion
  • 1 only MBR735 Schottky 35V 7A diode [1]
  • 1 only 28 pin arduino header terminal strip
  • 1 only SPST switch
  • 1 only 2-way PCB mount screw terminals -5mm
  • 1 only pkt M3 nuts
  • 1 only pkt M3*10mm bolts
  • 8 only 25mm x M3 bolts
  • 2 only cable ties

The estimated cost for this plotter is less than $100.

This diode protects your servos should you accidentally reverse your battery connections and may be omitted if you are confident that your wiring is correct.

Step 2: Inverse Kinematics

Photo 1 shows the construction lines for calculating the servo angles. [1]

Photo 2 shows an alternate solution if both servo arms are the same length (Isosceles triangle). Since the internal angles of a triangle equal PI radians (180 degrees) there is no need to calculate angleA ... just subtract angleB from PI and halve the result.

The absolute XY coordinates are shown in blue.

The internal angle for the blue triangle at the (0,0) coordinate is

  • atan2(y,x)

Pythagoras is used to calculate the length of ‘b’

  • b=sqrt(sq(X) + sq(Y))

The servo arms AB and BC (shown in red) form a triangle ABC.

The the internal angles of triangle ABC are calculated using the “Law of Cosines”:

  • Cos(A) = (b^2 + c^2 – a^2)/(2*b*c)
  • Cos(B) = (c^2 + a^2 – b^2)/(2*c*a)

From which we get:

  • Servo1_angle = acos((b^2 + c^2 – a^2)/(2*b*c)) + atan(y,x)
  • Servo2_angle = acos((c^2 + a^2 – b^2)/(2*c*a))

Each of these Arduino servo angles are in radians. [2]

The pulse-widths to each servo must be adjusted to get the correct angles. Details for doing this are explained in this instructable.



For practical reasons an X offset is added to each gcode X value to prevent the servo arms colliding.

Avoid using a Y offset as that causes servo arm1 to operate near 180 degrees ... some servos don’t rotate a full 180 degrees.


To convert these angles to degrees they must each be multiplied by

  • 180/PI = 57.295779513082320876798154814105

Step 3: Schematic

Photo1 shows the circuit schematic [1]

Photo 2 shows the matching shield

Photo 3 shows how the components fit together.



The plotter works with any 5 volt .. 6 volt servo with a stall current of less than 2 amps.

Step 4: Construction

MG 996R Plotter

A plastic suction cup and metal bracket are used to support the servo arms.

  • Photo 1 shows the original double suction cup for lifting glass.
  • Photo 2 shows the handle sawn off one cup.
  • Photo 3 shows the bracket dimensions for a "Trojan" suction cup. Alternate suction cups may be substituted ... just check their dimensions first.
  • Photo 4 shows the bracket attached to the suction cup [1]
  • Photo 5 shows a drop-in servo insert.

Suction cups have the advantage that:

  • they can be attached to any flat surface without scratching. [2]
  • they have a small footprint when storing.

SG90 Plotter

Each of the servos has been hot-glued, 200mm apart, onto a 3mm x 12mm x 220 length of aluminium extrusion.

The pen is cable-tied to a length of 1mm x 12mm aluminium and bent into an L-shape such that the pen-tip is 200mm from the servo.

The SG-90 plotter has no pen-lift as it was built to verify the inverse kinemetics.



Insert a length of 3mm aluminium extrusion between the suction cup and the bracket when drilling the four mounting holes. This allows for a drop-in servo bracket should that be needed

The servo bracket was made from a short length of 30mm x 3mm aluminium extrusion. The end was bent in a vice using a hammer.

Since taking these photos the servo bracket has been inverted and bolted to the outside of the suction cup bracket.


Suctions cups have enormous gripping power. The “Trojan” suction cup has a 4 inch diameter cup which means it can hold PI * 2in * 2in * 14psi = 176 lb ( approx. 80Kg).

Step 5: Servo Arm 1

The servo1 arm comprises two MG-996R servos clamped between two 25mm x 1mm lengths of aluminium extrusion (photo 1). Servo1 has since been inverted [1]

Photo 2 shows the drilling template (not to scale) for servo arm 1

The distance between each servo is 200mm.

Double-sided tape prevents the motors from slipping.



My original design subtracted the desired angle from 180 degrees should a servo(s) rotate in the opposite direction. The downside to this is that many servos can’t rotate a full 180 degrees.

In my final design I have inverted servo1 so that it is operating at shorter pulse-widths.

Step 6: Servo Arm 2

The dimension for servo arm2 are shown in photo1.

The arm is made from a length of 1mm x 25mm aluminium extrusion.

The 25mm width prevents the arm moving sideways.

The 1mm thickness is sufficiently flexible for the servo horn to lift the pen off the paper

Slide the pen downwards until the tip doesn’t quite touch the paper with the SG90 servo-horn extended.

Step 7: Software

The software for this plotter comprises three packages:

  • “servo_plotter.ino”
  • “myServo.h”
  • “myServo.cpp”


  • Download and copy “servo_plotter.ino” into a new Arduino sketch
  • Save the sketch as “servo_plotter” but without the quotes.
  • Download and copy “myServo.h” into the “servo_plotter” folder created in step 2 above
  • Download and copy “myServo.cpp” into the “servo_plotter” folder created in step 2 above

Installation is complete

15 January 2022

Alternate software using a PCA9685 servo shield has been posted here

Step 8: Hardware Adjustment

The top (yellow) trace in photo 1 shows a 500uS pulse which represents 0 degrees rotation.

The bottom (blue) trace in photo 1 shows a 2500uS pulse which represents 180 degrees rotation.

My theory is that the time difference (2000uS) between these two traces represents 180 degrees. I therefore use the following formula to set the servo angle:

  • pulse_width = 500 + servo_angle* 2000/180


Step 1

  • separate (disconnect) the servo arms from each other and the base
  • attach a pointer similar to that in photo 2 to servo 1
  • check that servo 1 points downwards and servo 2 point upwards
  • run servo_plotter.ino
  • set your Serial Monitor to 115200 BAUDS
  • select option “T5 .... set all servos to 90 degrees”
  • position the pointer such that it is close to 90 degrees counter-clockwise to the servo arm when the servo1 motor is pointing downwards
  • now adjust the 500uS pulse width in the servo_plotter_v1.ino header until you get exactly 90 degrees.

My values are shown below:

 // ----- set servo1 limits
  float zero1 = 550.0;
  float scale1 = (float)(2000 / 180.0);
  servo1.calibrate(zero1, scale1);

Step 2

  • select menu options T4, T5, and T6 in turn ... your pointer should move between 0, 90, and 180 degrees. (Note: some servos wont rotate a full 180 degrees)
  • adjust the 2000uS until 180 degrees is obtained or a lesser angle if the servo won't go any further
  • repeat step 1 if necessary

Step 3

  • attach the pointer to servo motor 2 such that the pointer is 90 degrees to the servo arm
  • repeat step 1 and step 2 for this servo

My values are shown below:

// ----- set servo2 limits
  float zero2 = 550.0;
  float scale2 = (float)(2000 / 180.0);
  servo2.calibrate(zero2, scale2);

Step 4

  • Remove the power when the servos are in their 90 degree positions.
  • Attach the servo1 arm pointing away from the mounting base.
  • Attach the servo2 arm at 90 degrees to the servo1 arm
  • The plotter is ready to go

Warning: Do not use menu option “T4 ....... calibrate ... all servos 0 degrees” while the plotter is assembled as the arms will collide.

Step 5

  • Keep clear of the servo arms
  • Apply power ... the servo arms should now move to their home (0,0) position

Step 6

Manually test the plotter by entering T3 or the following gcode instructions

G01 X100 Y0	// pen down ... move 100mm to the right
G01 X100 Y100	// pen down ... move upwards 100mm
G01 X0   Y100	// pen down ... move 100mm to the left
G01 X0   Y0	// pen down ... move downwards 100mm
G00 X0   Y0     // Pen up

All going well you should have drawn a box

Inkscape g-code files may be sent to the plotter using any terminal software ... I recommend “CoolTerm.exe” from

Step 9: Addenda

12 December 2020

This step record the results obtained with RDS3255


  • Photo1 shows the RDS3225 servos
  • Photo 2 shows the extra button s on the shield.
  • Photo 3 shows the "Menu" for plotter 2
  • Photo 4 shows a clockwise plot
  • Photo 5 shows a counter-clockwise plot
  • Photo 6 shows an overlay of the clockwise and counter-clockwise plots. Each direction has been plotted three times. Note that the peripheral curves lie on top of each other, whereas the diagonal plots are separated.

The diagonal line separation is a mystery as there is virtually no unwanted mechanical movement of the servo arms. A possible cause is the 3uS deadband for each servo + plus any backlash in the gears? If you study the video in the Intro section you will see that one servo arm always moves more than the other when plotting the peripheral lines ... whereas both arms must move to plot the diagonals. Plotting in opposite directions accentuates any errors.


  • The distance between the two RDS3225 motor shafts is 200mm.
  • The RDS3225 servo arm comprises a U-shaped length of 25mm x 3mm aluminium extrusion
  • The SG90 pen-lift arm is described in "Step 6: Servo Arm 2"
  • Two normally-open push buttons have been added ... one between pin 7 and ground ... the other between pin 8 and ground


The software for this plotter version is attached to this step

Changes include :

  • a new servo library that refreshes all servos simultaneously rather than in sequence
  • a "home" option
  • two test files (clockwise/counter-clockwise test squares)
  • four calibration options (described below)

Calibration sequence

  • manually rotate each servo to 90 degrees
  • now attach the servo horns
  • open your serial monitor at 115200 bauds
  • press menu item T6 ... numbers will apear on the screen and plotter arm 1 will move
  • pressing the buttons will cause the numbers to increase/decrease
  • press both buttons when plotter arm 1 is pointing directly away from the suction cap (along the Y-axis)
  • ecord the last number in the appropriate header location (_servo1_90)
  • press menu item 7 and repeat the process for servo arm2 such that servo arm 2 is pointing along the X-axis
  • press both buttons to exit and record the last number in the header (_servo2_90)
  • press menu item T8 ... servo arm 1 will move towards the 45 degree position
  • adjust the numbers using the push buttons ... use a 45 degree plastic set-square to determine the angle.
  • press both buttons to exit and record the last number in the header (_servo1_45)
  • press menu item T9 ... servo arm 2 will move.
  • adjust the numbers until servo arm 2 is 45 degrees relative to its original position
  • press both buttons to exit and record the last number in the header (_servo2_45)
  • finally, recompile and upload your changes to your arduino
  • calibration now complete.


  • think before you change the above button sequence ... will the arms collide?
  • watch your fingers ... the servos are capable of buckling the aluminium extrusion if you get things wrong

23 December 2020

See for software and hardware improvements to this plotter.

Step 10: Summary

This instructable explains how to make an A4 plotter using three servos and an Arduino Uno R3.

Two plotters are described:

  • One plotter uses MG 996R metal gear servos.
  • The other plotter uses light-weight SG90 servos

The Arduino is powered from your USB port.

An external 6 volt power supply capable of 5 amps peak is required for the servos.

The layout diagram for a wiring shield is provided along with the circuit diagram

The MG 996R plotter is mechanically sturdy:

  • The first arm comprises a light-weight box section
  • The second arm has no sideways movement but is able to flex up and down for the pen-lift
  • The pen is raised by pressing the SG90 servo horn against the paper.
  • The suction cup has an estimated holding force of 80Kg

The motor for the second servo arm is mounted on the first arm.

  • This reduces the inertia by bringing the mass of the second motor closer to the mounting point.
  • A side effect of mounting both motors on the same arm is that the first arm rotates in the wrong direction.

There are two solutions:

  • subtract the first angle from 180 degrees
  • invert the motor ... this is my preferred option as the servo angles appear more accurate.

The servo current for the MG 996R plotter is 18 milliamps (mA) when all servos are idling and 400 mA when all servos are operating.

The software for this plotter has been adapted from the following instructables:

Both plotters work ... but not as well as the above digital versions.

12 December 2020 ... Deadband appears to be an issue ... see Step 9

I am publishing my results in the hope that the construction details and solutions to issues that I have encountered are of help to others.

The estimated cost for MG 996 plotter is less than $100.

WARNING: Keep clear of the servo arms when using these plotters as the motors are extremely powerful and capable of rapid movement.

  Click here   to view my other instructables.

1000th Contest

Participated in the
1000th Contest

1 Person Made This Project!


  • Make it Glow Contest

    Make it Glow Contest
  • Clocks Contest

    Clocks Contest
  • Baking Contest

    Baking Contest



1 year ago

hello sir i have a problem with the code it keeps saying the pen lift does not name a type and a bunch of other problems with the code


Question 1 year ago

Would like to see where the beginning of the sign aria dot 0/0
I made a sketch and my question is this correct
Can someone help me, how the arms move relative to each other etc
My arms are 60mm long
This is a great project to learn
Please Help

Draw Aria.png

Answer 1 year ago

Thank you for your interest in my project :)

The (0,0) point for my servo is next to Servo 1 as shown in Step 2 "Inverse Kinematics". Move your green area to the right and down.

Your sketch shows Servo 1 near the center of the 150mm base-line. This is possible but requires a new set of calculations. I leave that up to you.

Be aware that your servo arms can only extend 120mm and will not reach the top / top-corners regardless of where you position Servo 1 along the base-line.


1 year ago

Hello sir, good and excellent work. What are the programs that implement the drawing and does it need to convert the image into a jacode?


Reply 2 years ago

Thank you for this interesting link :)

Have come to the same conclusion about the need for calibration tables/offsets.

Before doing this I decided to strengthen arm2 so that it is self supporting and add a pen-lift. The penlift shown in the attached photo works well ... the servo horn squeak has gone and there is no sideways displacement.

Will post a code update once I have everything working.


2 years ago

Great project and a very detailed indestructible. Top marks fella.
Would you have a video of how the finished version works ? The only comment I could have made was the wobble in your test piece which proved your maths and set up was correct but also some more mechanical stiffness was needed. I see from the pic you've beefed up the motion linkagage arms. Did this cure the porblem ?


Reply 2 years ago

Thank you for your interest and comment :)

I am in the process of of rebuilding the plotter using the following servos.

I have also also rewritten the software using an improved algorithm that simplifies the calibration. I have tested the software on the existing plotter and the new servos ... it works extremely well.

I will post a video once everything is working


Reply 2 years ago

They look chunky servos. What sort of resolution are you reliably expecting ?
0.1 degree ?


Reply 2 years ago

Not sure ... in theory a servo has infinite resolution ... in practice any error is magnified with distance. Keen to try them out as a quick out-of-box test indicated their opertion was velvet smooth. Unfortunately it may be a few days as have injured my right thumb and can't hold anything.


Reply 2 years ago

I think (and I'm certainly no expert) The resolution is basically set by the angle of rotation divided by the timings of whatever is kicking out the PWM up to the point where the dead space between pulses gets too short. So a typical servo may need 1ms pulse for 0 degrees , 1.5ms for 90 degrees and 2ms for 180degrees. So a PWM resolution of 0.5ms gives you a servo resolution of 90 degrees and thus a 0.05ms pulse gives you 9 and 0.005ms (5 us) gives 0.9 degrees. But as stated the servo itself can only go down to a certain length of pulse before it can't read it properly and gets swamped. What I haven't a clue is , how far can you push these servos ? Maybe put a laser on one and aim it across a large room move it the smallest distance you can and work out the angle.. I haven't got any at present or I'd be doing that myself now I've sparked my own interest.. This will then let you know how big you can go with your plotter and still get the resolution you want.


Reply 2 years ago

I see where you are coming from ... never looked at it from that viewpoint.

Some actual figures for a 180 degree SG90 servo:
90 degrees ... 1319 uS
45 degrees ... 839 uS
Difference ... 480 uS

This translates to 45/480 or approximately 0.1 degrees/uS.

Let's now assume our plotter arm is 200mm in length then a 0.1 degree error translates to 2*PI*200/3600 = 0.35 mm which is half the width of a 0.7mm pencil lead.

The main problem is going to be the deadband which can be up to 5uS on low quality servos.

Will cobble my servos together and see what I get.

Thanks for your ideas :)


Reply 2 years ago

0.1 degree looks like that may be workable. So maybe that is setting your max X and Y Area. Interested to see what you actually get with the dead band. Though I'm not quite sure about the dead band figure. Does this mean a change of 5us or less on the pwm and the servo may not move? In which case you'd have 480/5 = 96 or 9.6 degrees resolution ? Surely that can't be right ?


Reply 2 years ago

It's starting to look like that :(

480/5 = 96 active regions in 45 degrees which equates to a worst case error of 45/96 = 0.47 degrees.

With 200mm arms this equates to a positional error of 2*PI*200*0.47/360 = 1.64mm when the arms are a right angles (i.e. one arm being pushed) and possibly double (3.28mm) when both arms are extended. Now add servo gear backlash and the picture is not looking good.

This would explain the wobbles in the video ... perhaps they are not all due to the flimsy construction.?

Looking at the video I notice that the diagonal line from bottom left to top right is smoother than the others. A possible reason for this that only servo 2 is moving and any deadband effects are masked as the direction of movement is in a straight line. It would also explain why the line doesn't quite reach the top corner?

I also notice that that same line has a slight curve. This shouldn't happen as the software should eliminate this curve by adjusting servo1. This indicates that servo1 is stuck in a deadband.

About to measure the dead band by changing the pulsewidths in 1uS increments and see how many 1uS increments are needed to produce movement. Will repeat this in both directions ... curious to see if there hysteresis.

Just an idle thought ... if deadband is an issue I wonder whether it would be possible to get things moving by temporarily adding/subtracting the deadband amount to/from the first step?

Thanks again for you comments ... they've been really helpful :)


Reply 2 years ago

You could probably compensate a bit by using a PID type controller. So for big changes you'd accelerate the movement then slow it down to hit target. You may even be able to tune out the dead band but I'm guessing you will always have things needing a very small movement (ie drawing an arc) and PID wouldn't help at all. Something I don't know , or should I say one of the many things I don't know :-) is if the circuitry of the servo can handle a min change or PWM of say 6 us then it this linear for a 45 degree servo or 90 or 180 etc. If the resolution and error remains constant then gearing a 180 degree servo down to 45 would 0.25 your error . But if the difference in the servo motion is simply internal gearing then I guess it wouldn't help at all.
The other thing that may help a bit which I think you are eluding to with your last comment and that is produce a compensation curve and use it to add or deduct from the calculated position to bring it back to roughly where it should be . So for example if you want to move one step in the X direction then you are tied to the resolution and not much you can do about it but you maybe able to compensate by taking one steep off every 50 or whatever if the movement is in the same direction is should be fairly constant error. .


Reply 2 years ago

Have considered lookup tables and interpolation but dont think this will be necessary as the servos seem reasonably linear over their entire range.

For example I am currently using the micrsecond readings at 45 degrees and 90 degrees to calculate the 0 degree and 180 points ... this seems to work as measured dimensions for a 150mm x 100mm rectangle are bang on. Even the diagonals are reasonable straight.

Things I have noticed:
- The current increases to arounf 0.5 amps when the servos are in motion
- The idle current for threes servos is 12mA total.

I suspect deadband has been introduced to prevent hunting and lower the idle current.

It may be possible to modify the electronics to reduce /eliminate deadband. This series of youtube articles infers you can get almost infinite resolution


Reply 2 years ago

Good news then it looks do able. I guess you are right about the hunting if you kill the deadband. Interesting to see how tight you can make it. It's a shame you didn't get some comments from an RC servo specialist it may have taken out some of the trial and error.


Reply 2 years ago

True ... but I didn't know I had a problem until I tried :)

Have since found this interesting article ...

It shows how to reduce servo deadband by decreasing a resistor value. May be worth a try if all else fails ?