diff --git a/emb/servos.cpp b/emb/servos.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ff29bf708c9d8274a336ccd1f492e2f8588ead9
--- /dev/null
+++ b/emb/servos.cpp
@@ -0,0 +1,118 @@
+#include "servos.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <stdint.h>
+
+#define NUM_SERVOS 3
+
+static void reorderServos();
+static void initTimer1();
+
+struct Servo
+{
+    volatile uint8_t * const port;  // the PORT register connected to the servo
+    volatile uint8_t * const ddr;   // the DDR register connected to the servo
+    const uint8_t pinMask;          // the pin mask for the servo
+    uint16_t tempValue;             // stores a temporary value before it's written to the servo
+    uint16_t value;                 // current pulse width value
+};
+
+static Servo servos[NUM_SERVOS] = {
+    { &PORTB, &DDRB, (1 << PB2), 0, 0 },
+    { &PORTB, &DDRB, (1 << PB3), 0, 0 },
+    { &PORTB, &DDRB, (1 << PB4), 0, 0 }
+};
+
+static uint16_t orderedValues[NUM_SERVOS];  // current servo values in ascending order
+static uint8_t orderedValueIndex = 0;       // index in orderedValues array
+static bool updateFlag = false;             // used to flag when the servo values have been updated
+
+void initServos()
+{
+    for (Servo &servo : servos)
+    {
+        *servo.ddr |= servo.pinMask;
+    }
+    
+    initTimer1();
+}
+
+void setServoAngle(uint8_t servo, int16_t arcMin)
+{
+    if (0 <= servo && servo < NUM_SERVOS)
+        servos[servo].tempValue = 2999 + (int32_t) arcMin * 1000 / (180 * 60);
+}
+
+void updateServos()
+{
+    updateFlag = true;
+}
+
+static void reorderServos()
+{
+    uint16_t minValue, maxValue = 0;
+    
+    for (Servo &servo : servos)
+        servo.value = servo.tempValue;
+    
+    for (uint16_t &value : orderedValues)
+    {
+        minValue = (uint16_t) -1;
+        
+        for (Servo servo : servos)
+        {
+            if (servo.value < minValue && servo.value > maxValue)
+                minValue = servo.value;
+        }
+        
+        maxValue = minValue;
+        value = maxValue;
+    }
+}
+
+static void initTimer1()
+{
+    cli();
+    
+    TCCR1A = 0;
+    TCCR1B = (1 << WGM12)   // CTC mode, TOP = OCR1A
+           | (1 << CS11);   // 8th prescaler 
+    TCCR1C = 0;
+    
+    OCR1A = 39999;                          // set TOP for 50 Hz 
+    TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B); // enable interrupts
+    
+    sei();
+}
+
+ISR(TIMER1_COMPA_vect)
+{
+    if (updateFlag)
+    {
+        updateFlag = false;
+        reorderServos();
+    }
+    
+    for (Servo &servo : servos)
+        *servo.port |= (servo.pinMask); // set servo pin HIGH
+    
+    orderedValueIndex = 0;
+    OCR1B = orderedValues[orderedValueIndex];
+}
+
+ISR(TIMER1_COMPB_vect)
+{
+    uint16_t currValue = orderedValues[orderedValueIndex];
+    
+    for (Servo &servo : servos)
+    {
+        if (servo.value == currValue)
+            *servo.port &= ~(servo.pinMask); // set servo pin LOW
+    }
+    
+    orderedValueIndex++;
+    
+    if (orderedValueIndex < NUM_SERVOS)
+        OCR1B = orderedValues[orderedValueIndex];
+}
diff --git a/emb/servos.hpp b/emb/servos.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2aeff442780d2dabc02fa3e9d329c89e8b1cec7
--- /dev/null
+++ b/emb/servos.hpp
@@ -0,0 +1,24 @@
+#ifndef SERVOS_H
+#define SERVOS_H
+
+#include <stdint.h>
+
+/**
+ * Initialises the servos.
+ */
+void initServos();
+
+/**
+ * Sets the angle of a servo.
+ * 
+ * @param servo     the index of the servo to control
+ * @param arcMin    the angle of the servo in arc-minutes
+ */
+void setServoAngle(uint8_t servo, int16_t arcMin);
+
+/**
+ * Writes the servo values out to the servos.
+ */
+void updateServos();
+
+#endif /* SERVOS_H */