Robot Arm Controller Library

Dependents:   PR_RobotArm lighthouse_2

Files at this revision

API Documentation at this revision

Comitter:
jah128
Date:
Thu Feb 16 23:57:45 2017 +0000
Commit message:
First commit

Changed in this revision

display.cpp Show annotated file Show diff for this revision Revisions of this file
display.h Show annotated file Show diff for this revision Revisions of this file
remote.cpp Show annotated file Show diff for this revision Revisions of this file
remote.h Show annotated file Show diff for this revision Revisions of this file
robotarm.cpp Show annotated file Show diff for this revision Revisions of this file
robotarm.h Show annotated file Show diff for this revision Revisions of this file
servo.cpp Show annotated file Show diff for this revision Revisions of this file
servo.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 9f7b70e0186e display.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/display.cpp	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,187 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ *
+ * File: display.cpp
+ *
+ *
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ *
+ * February 2017
+ *
+ * Driver for the Midas 16x2 I2C LCD Display (MCCOG21605x6W) LCD
+ * [Farnell part 2218942 or 2063206]
+ *
+ */
+
+#include "robotarm.h"
+
+
+Timeout init_timeout;
+
+
+Display::Display(PinName sda, PinName scl) :  Stream("display"), _i2c(sda,scl)
+{
+}
+
+Display::Display() :  Stream("display"), _i2c(p28,p27)
+{
+}
+
+int Display::i2c_message(char byte)
+{
+    char bytes [2];
+    bytes[0]=0x80;
+    bytes[1]=byte;
+    int ret=_i2c.write(LCD_ADDRESS,bytes,2);
+    wait(0.01);
+    return ret;
+}
+
+int Display::disp_putc(int c)
+{
+    char message [2];
+    message[0]=0x40;
+    message[1]=c;
+    _i2c.write(LCD_ADDRESS,message,2);
+    wait(0.01);
+    return c;
+}
+
+void Display::init()
+{
+    //Set initial states: display on, cursor off
+    display_on = 1;
+    cursor_on = 0;
+    blink_on  = 0;
+
+    i2c_message(0x38);
+    i2c_message(0x39);
+    i2c_message(0x14);
+    i2c_message(0x74);
+    i2c_message(0x54);
+    i2c_message(0x6F);
+    _set_display();
+    clear_display();
+    wait(0.05);
+    clear_display();
+    set_position(0,0);
+    write_string("  YORK ROBOTICS");
+    set_position(1,0);
+    write_string("   LABORATORY");
+    init_timeout.attach(this,&Display::post_init,0.3);
+    wait(0.62);
+}
+
+void Display::post_init()
+{
+    clear_display();
+    home();
+    write_string("ROBOTIC ARM");
+    set_position(1,0);
+    char line [17];
+    sprintf(line,"VERSION %1.2f", SOFTWARE_VERSION_CODE  );
+    set_position(1,0);
+    write_string(line);
+    init_timeout.attach(this,&Display::post_post_init,0.3);
+}
+
+void Display::post_post_init()
+{
+    clear_display();
+    home();
+}
+
+void Display::write_string(char * message)
+{
+    size_t length = strlen(message);
+    if (length > 16) length = 16;
+    char to_send [length+1];
+    to_send[0]=0x40;
+    for(int i=0; i<length; i++) {
+        to_send[i+1] = message[i];
+    }
+    _i2c.write(LCD_ADDRESS,to_send,length+1);
+   
+}
+
+
+void Display::write_string(char * message, char length)
+{
+    char to_send [length+1];
+    to_send[0]=0x40;
+    for(int i=0; i<length; i++) {
+        to_send[i+1] = message[i];
+    }
+    _i2c.write(LCD_ADDRESS,to_send,length+1);
+  
+}
+
+void Display::set_position(char row, char column)
+{
+    if(row < 2 && column < 16) {
+        char pos = 128 +((row * 64)+column);
+        i2c_message(pos);
+    }
+}
+
+void Display::set_cursor(char enable)
+{
+    cursor_on=enable;
+    _set_display();
+}
+
+void Display::set_blink(char enable)
+{
+    blink_on=enable;
+    _set_display();
+}
+
+void Display::set_display(char enable)
+{
+    display_on=enable;
+    _set_display();
+}
+
+
+void Display::clear_display()
+{
+    i2c_message(0x01);
+}
+
+void Display::home()
+{
+    i2c_message(0x02);
+}
+
+
+void Display::_set_display()
+{
+    char mode = 8;
+    if(display_on>0) mode += 4;
+    if(cursor_on>0) mode += 2;
+    if(blink_on>0) mode ++;
+    i2c_message(mode);
+}
+
+
+int Display::_putc (int c)
+{
+    putc(c);
+    return(c);
+}
+
+int Display::_getc (void)
+{
+    char r = 0;
+    return(r);
+}
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and limitations under the License.
+ */
diff -r 000000000000 -r 9f7b70e0186e display.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/display.h	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,140 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ * 
+ * Driver for the Midas 16x2 I2C LCD Display (MCCOG21605x6W) LCD  [Farnell part 2218942 or 2063206]
+ *
+ * File: display.cpp
+ *
+ * (C) Dept. Electronics & Computer Science, University of York
+ * 
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ *
+ * January 2017
+ *
+ *
+ */ 
+
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+
+/**
+ * Display class
+ * Functions for use with the Midas 16x2 I2C LCD Display (MCCOG21605x6W) LCD
+ * Farnell part 2218942 or 2063206
+ *
+ * Example:
+ * @code
+ * #include "robotarm.h"
+ *
+ * Robotarm arm;
+ *
+ * int main() {
+ *     arm.init();
+ *     display.clear_display;       //Clears display
+ *     display.set_position(0,2);   //Set cursor to row 0 column 2
+ *     display.write_string("YORK ROBOTICS");
+ *     display.set_position(1,3);   //Set cursor to row 1 column 3
+ *     display.write_string("LABORATORY");
+ * }
+ * @endcode
+*/
+class Display : public Stream
+{
+
+// Public Functions
+
+public:
+
+    /** Create the LCD Display object connected to the default pins
+     * (sda = p28, scl = p27)
+     */
+    Display();
+
+    /** Create the LCD Display object connected to specific pins
+     *
+     * @param sda pin   - default is p28
+     * @param scl pin   - default is p27
+     */
+    Display(PinName sda, PinName scl);
+
+    /** Clear the display
+    */
+    void clear_display(void);
+
+    /** Set cursor to home position
+    */
+    void home(void);
+
+    /** Print string message
+    * @param message - The null-terminated message to print
+    */
+    void write_string(char * message);
+
+    /** Print string message of given length
+    * @param message - The message to print
+    * @param length - The number of characters to display
+    */
+    void write_string(char * message, char length);
+
+    /** Set the row and column of cursor position
+    * @param row - The row of the display to set the cursor to (either 0 or 1)
+    * @param column - The column of the display to set the cursor to (range 0 to 15)
+    */
+    void set_position(char row, char column);
+
+    /** Enable or disable cursor
+    * @param enable - Set to 1 to enable the cursor visibility
+    */
+    void set_cursor(char enable);
+
+    /** Enable or disable cursor blink
+    * @param enable - Set to 1 to enable the cursor blinking mode
+    */
+    void set_blink(char enable);
+
+    /** Enable or disable display
+    * @param enable - Set to 1 to enable the display output
+    */
+    void set_display(char enable);
+
+    //Parts of initialisation routine
+    void post_init(void);
+    void post_post_init(void);
+
+    // Send a 1-byte control message to the display
+    int i2c_message(char byte);
+
+    // Default initialisation sequence for the display
+    void init(void);
+
+    int disp_putc(int c);
+
+
+private :
+
+    I2C _i2c;
+
+    char display_on;
+    char cursor_on;
+    char blink_on;
+
+    void _set_display();
+
+    virtual int _putc(int c);
+    virtual int _getc();
+
+};
+
+#endif // DISPLAY_H
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and limitations under the License.
+ */
\ No newline at end of file
diff -r 000000000000 -r 9f7b70e0186e remote.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/remote.cpp	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,245 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ * 
+ * Robot Arm Remote Control
+ *
+ * File: remote.cpp
+ *
+ * (C) Dept. Electronics & Computer Science, University of York
+ * 
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ * February 2017, Version 1.0
+ *
+ */ 
+
+#include "robotarm.h"
+
+DigitalIn remote_sense(p17,PullUp);
+DigitalIn sw1_up(p21,PullUp);
+DigitalIn sw1_down(p22,PullUp);
+DigitalIn sw1_left(p23,PullUp);
+DigitalIn sw1_right(p24,PullUp);
+DigitalIn sw2_up(p25,PullUp);
+DigitalIn sw2_down(p26,PullUp);
+DigitalIn sw2_left(p29,PullUp);
+DigitalIn sw2_right(p30,PullUp);
+
+DigitalOut red_led(p16);
+DigitalOut green_led(p20);
+Ticker attach_remote_sensor;
+char remote_on = 0;
+char h_switch = 0;
+char limit_reached = 0;
+
+
+void Remote::init()
+{
+    attach_remote_sensor.attach_us(this,&Remote::detect_remote,REMOTE_POLL_RATE);
+}
+
+char step = REMOTE_START_SPEED;
+
+void Remote::detect_direction()
+{
+    char t_switch = 0;
+    if(!sw1_up)t_switch+=1;
+    if(!sw1_down)t_switch+=2;
+    if(!sw1_left)t_switch+=4;
+    if(!sw1_right)t_switch+=8;
+    if(!sw2_up)t_switch+=16;
+    if(!sw2_down)t_switch+=32;
+    if(!sw2_left)t_switch+=64;
+    if(!sw2_right)t_switch+=128;
+
+    if(t_switch!=h_switch) {
+        h_switch=t_switch;
+        pc.printf("Switch ");
+        if(h_switch == 0) {
+            pc.printf("released");
+            step = REMOTE_START_SPEED;
+            set_led(0);
+        } else {
+            if(h_switch & 0x01) {
+                pc.printf("1-UP ");
+            }
+            if(h_switch & 0x02) {
+                pc.printf("1-DN ");
+            }
+            if(h_switch & 0x04) {
+                pc.printf("1-LF ");
+            }
+            if(h_switch & 0x08) {
+                pc.printf("1-RT ");
+            }
+            if(h_switch & 0x10) {
+                pc.printf("2-UP ");
+            }
+            if(h_switch & 0x20) {
+                pc.printf("2-DN ");
+            }
+            if(h_switch & 0x40) {
+                pc.printf("2-LF ");
+            }
+            if(h_switch & 0x80) {
+                pc.printf("2-RT ");
+            }
+
+        }
+        pc.printf("\n");
+    }
+    if(t_switch != 0) {
+
+        if(h_switch & 0x01) {
+            move_servo(1,step);
+        }
+        if(h_switch & 0x02) {
+            move_servo(1,-step);
+        }
+        if(h_switch & 0x04) {
+            move_servo(0,-step);
+        }
+        if(h_switch & 0x08) {
+            move_servo(0,step);
+        }
+        if(h_switch & 0x10) {
+            move_servo(2,step);
+        }
+        if(h_switch & 0x20) {
+            move_servo(2,-step);
+        }
+        if(h_switch & 0x40) {
+            move_servo(3,-step);
+        }
+        if(h_switch & 0x80) {
+            move_servo(3,step);
+        }
+        if(REMOTE_LINEAR_STEPS != 1) {
+            step += REMOTE_START_SPEED;
+            if(step > 200) step = 200;
+        }
+        set_led(2-limit_reached);
+    }
+}
+
+void Remote::move_servo(char servo_number, int adjust)
+{
+    char enabled = 0;
+    char servo_id = 0;
+    short stored_value = 0;
+    switch(servo_number) {
+        case 0:
+            servo_id=BASE;
+            if(HAS_BASE)enabled = 1;
+            stored_value = target_base;
+            break;
+        case 1:
+            servo_id=SHOULDER;
+            if(HAS_SHOULDER) enabled = 1;
+            stored_value = target_shoulder;
+            break;
+        case 2:
+            servo_id=ELBOW;
+            if(HAS_ELBOW) enabled = 1;
+            stored_value = target_elbow;
+            break;
+        case 3:
+            servo_id=WRIST;
+            if(HAS_WRIST) enabled = 1;
+            stored_value = target_wrist;
+            break;
+    }
+    if(enabled) {
+        if(REMOTE_USE_CURRENT_POSITION == 1) stored_value = servo.GetPosition(servo_id);
+        int adjusted = stored_value + adjust;
+        limit_reached = 0;
+        int servo_low_limit = servo.GetLowerLimit(servo_id);
+        int servo_high_limit = servo.GetUpperLimit(servo_id);
+        if(adjusted <= servo_low_limit) {
+            stored_value = servo_low_limit;
+            limit_reached = 1;
+        }
+        if(adjusted > servo_high_limit) {
+            stored_value = servo_high_limit;
+            limit_reached = 1;
+        }
+        if(limit_reached == 0) stored_value = adjusted;
+        pc.printf("{%d}",stored_value);
+        servo.SetGoal(servo_id,stored_value,1);
+        pc.printf("{-}");
+        servo.trigger();
+        switch(servo_number) {
+        case 0:
+            target_base = stored_value;
+            break;
+        case 1:
+            target_shoulder = stored_value;
+            break;
+        case 2:
+            target_elbow = stored_value;
+            break;
+        case 3:
+            target_wrist = stored_value;
+            break;
+    }
+
+    } else limit_reached = 2; //Turns off LED on inactive servos
+}
+void Remote::detect_remote()
+{
+    if (remote_sense == remote_on) {
+        if(remote_on) {
+            remote_on = 0;
+            // Remote detached
+            pc.printf("Remote detached\n");
+            display.clear_display();
+            set_led(0);
+        } else {
+            remote_on = 1;
+            // Remote attached
+            pc.printf("Remote attached\n");
+            display.clear_display();
+            display.home();
+            display.write_string("REMOTE CONTROL");
+        }
+    } else {
+        if(remote_on) detect_direction();
+    }
+}
+
+/**
+ * Set the LED on the remote
+ * param: mode 0=off 1=red 2=green 3=both
+ */
+void Remote::set_led(char mode)
+{
+    switch(mode) {
+        case 0:
+            red_led=0;
+            green_led = 0;
+            break;
+        case 1:
+            red_led = 1;
+            green_led = 0;
+            break;
+        case 2:
+            red_led = 0;
+            green_led = 1;
+            break;
+        case 3:
+            red_led = 1;
+            green_led = 1;
+            break;
+    }
+}
+
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
\ No newline at end of file
diff -r 000000000000 -r 9f7b70e0186e remote.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/remote.h	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,77 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ * 
+ * Robot Arm Remote Control
+ *
+ * File: remote.h
+ *
+ * (C) Dept. Electronics & Computer Science, University of York
+ * 
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ * February 2017, Version 1.0
+ *
+ */ 
+
+
+#ifndef REMOTE_H
+#define REMOTE_H
+
+/** Remote Class
+ * The Remote controller class
+ *
+ * Example code for main.cpp:
+ * @code
+ * #include "robotarm.h"
+ * Robotarm arm;
+ * int main(){
+ *    arm.init();
+ *    while(1) { //Do something!
+ *    }
+ * }
+ * @endcode
+ */
+class Remote
+{
+public:
+    /**
+     * Main initialisation routine for the robot arm
+     *
+     * Set up the display, set up listener for remote control, set up servos
+     */
+    void init(void);
+    
+    /**
+     * Set the LED on the remote
+     * param: mode 0=off 1=red 2=green 3=both
+     */
+    void set_led(char mode);
+    
+    /**
+     * Detect if remote has been attached or detached
+     */
+    void detect_remote(void);
+    
+    /**
+     * Detect if a direction switch is being pressed
+     */
+    void detect_direction(void);
+    
+    /**
+     * Move the servo based on controller position
+     */
+    void move_servo(char servo_number, int adjust);
+
+    
+};
+#endif // REMOTE_H
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
\ No newline at end of file
diff -r 000000000000 -r 9f7b70e0186e robotarm.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/robotarm.cpp	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,193 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ *
+ * Robot Arm Controller 
+ *
+ * File: robotarm.cpp
+ *
+ * (C) Dept. Electronics & Computer Science, University of York
+ *
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ * February 2017, Version 1.0
+ */
+
+#include "robotarm.h"
+
+Display display;                    //Connects to i2c(p28,p27)
+Remote remote;                      //The Remote control
+Serial pc(USBTX,USBRX);             //PC Serial Interface [note there is a BlueSMIRF BT socket too....]
+Servo servo(p9,p10);                //The single-wire serial interface for the servos connects to p9 and p10
+short target_base = BASE_ZERO;
+short target_shoulder = SHOULDER_ZERO;
+short target_elbow = ELBOW_ZERO;
+short target_wrist = WRIST_ZERO;
+
+void Robotarm::init()
+{
+    pc.baud(115200);
+    //hardware_test();
+    pc.printf("Robot Arm Controller\nSoftware Version %1.2f\n",SOFTWARE_VERSION_CODE);
+    display.init();
+    init_servos();
+}
+
+void Robotarm::init_servos()
+{
+    Timeout fail_timeout;
+    fail_timeout.attach(this,&Robotarm::fail_init,2);
+    display.clear_display();
+    wait(0.1);
+    display.set_position(0,0);
+    display.write_string("CHECKING SERVOS:");
+    pc.printf("Initialising robot arm\n");
+    wait(0.1);
+
+    if(HAS_BASE) {
+        display.set_position(1,0);
+        display.write_string("BASE:");
+        pc.printf("Check servo %d [BASE]:",BASE);
+        if(servo.GetModelNumber(BASE) != MX28_MODEL) fail_init();
+        servo.SetLowVoltageLimit(BASE, LOW_VOLTAGE_LIMIT);
+        servo.SetHighVoltageLimit(BASE, HIGH_VOLTAGE_LIMIT);
+        servo.SetTemperatureLimit(BASE, HIGH_TEMPERATURE_LIMIT);
+        //servo.SetCWLimit(BASE, 0);
+        //servo.SetCCWLimit(BASE, 0);
+        servo.LockEeprom(BASE);
+        pc.printf("OK\n");
+        display.set_position(1,9);
+        display.write_string("[OK]");
+        wait(0.2);
+    }
+
+    if(HAS_SHOULDER) {
+        display.set_position(1,0);
+        display.write_string("SHOULDER:        ");
+        pc.printf("Check servo %d [SHOULDER]:",SHOULDER);
+        if(servo.GetModelNumber(SHOULDER) != MX28_MODEL) fail_init();
+        servo.SetLowVoltageLimit(SHOULDER, LOW_VOLTAGE_LIMIT);
+        servo.SetHighVoltageLimit(SHOULDER, HIGH_VOLTAGE_LIMIT);
+        servo.SetTemperatureLimit(SHOULDER, HIGH_TEMPERATURE_LIMIT);
+        //servo.SetCWLimit(SHOULDER, 0);
+        //servo.SetCCWLimit(SHOULDER, 0);
+        servo.LockEeprom(SHOULDER);
+        pc.printf("OK\n");
+        display.set_position(1,9);
+        display.write_string("[OK]");
+        wait(0.2);
+    }
+
+    if(HAS_ELBOW) {
+        display.set_position(1,0);
+        display.write_string("ELBOW:           ");
+        pc.printf("Check servo %d [ELBOW]:",ELBOW);
+        if(servo.GetModelNumber(ELBOW) != MX28_MODEL) fail_init();
+        servo.SetLowVoltageLimit(ELBOW, LOW_VOLTAGE_LIMIT);
+        servo.SetHighVoltageLimit(ELBOW, HIGH_VOLTAGE_LIMIT);
+        servo.SetTemperatureLimit(ELBOW, HIGH_TEMPERATURE_LIMIT);
+        //servo.SetCWLimit(ELBOW, 0);
+        //servo.SetCCWLimit(ELBOW, 0);
+        servo.LockEeprom(ELBOW);
+        pc.printf("OK\n");
+        display.set_position(1,9);
+        display.write_string("[OK]");
+        wait(0.2);
+    }
+
+    if(HAS_WRIST) {
+        display.set_position(1,0);
+        display.write_string("WRIST:          ");
+        pc.printf("Check servo %d [WRIST]:",WRIST);
+        if(servo.GetModelNumber(WRIST) != AX12_MODEL) fail_init();
+        servo.SetLowVoltageLimit(WRIST, LOW_VOLTAGE_LIMIT);
+        servo.SetHighVoltageLimit(WRIST, HIGH_VOLTAGE_LIMIT);
+        servo.SetTemperatureLimit(WRIST, HIGH_TEMPERATURE_LIMIT);
+        //servo.SetCWLimit(WRIST, 0);
+        //servo.SetCCWLimit(WRIST, 0);
+        servo.LockEeprom(WRIST);
+        pc.printf("OK\n");
+        display.set_position(1,9);
+        display.write_string("[OK]");
+        wait(0.2);
+    }
+
+    fail_timeout.detach();
+}
+
+float zero_time_delay;
+Timeout zero_timeout;
+
+void Robotarm::zero_servos(float time_delay)
+{
+    if(time_delay <= 0)_zero_servos();
+    else {
+        zero_time_delay = time_delay;
+        display.clear_display();
+        display.set_position(0,0);
+        display.write_string("ZEROING ARM IN");
+        display.set_position(1,0);
+        char message[17];
+        sprintf(message,"%2.1f SECONDS",zero_time_delay);
+        display.write_string(message);
+        if(zero_time_delay < 0.1)zero_timeout.attach(this,&Robotarm::_zero_servos,zero_time_delay);
+        else zero_timeout.attach(this,&Robotarm::_zero_servos_display_update,0.1);
+    }
+}
+
+void Robotarm::_zero_servos_display_update()
+{
+    display.set_position(1,0);
+    zero_time_delay-=0.1;
+    char message[17];
+    sprintf(message,"%2.1f SECONDS",zero_time_delay);
+    display.write_string(message);
+    if(zero_time_delay < 0.1) zero_timeout.attach(this,&Robotarm::_zero_servos,zero_time_delay);
+    else zero_timeout.attach(this,&Robotarm::_zero_servos_display_update,0.1);
+}
+
+void Robotarm::_zero_servos()
+{
+    display.clear_display();
+    servo.SetGoal(BASE,BASE_ZERO,0);
+    servo.SetGoal(SHOULDER,SHOULDER_ZERO,0);
+    servo.SetGoal(ELBOW,ELBOW_ZERO,0);
+    servo.SetGoal(WRIST,WRIST_ZERO,0);
+    servo.trigger();
+    target_base = BASE_ZERO;
+    target_shoulder = SHOULDER_ZERO;
+    target_elbow = ELBOW_ZERO;
+    target_wrist = WRIST_ZERO;
+}
+
+void Robotarm::fail_init()
+{
+    display.clear_display();
+    display.set_position(0,0);
+    display.write_string("SERVO ERROR");
+    while(1) {
+        remote.set_led(1);
+        wait(0.2);
+        remote.set_led(0);
+        wait(0.1);
+    }
+}
+
+void Robotarm::hardware_test()
+{
+    pc.printf("\nBeginning extended hardware test\n");
+    wait(0.5);
+    pc.printf("Scanning for servos:\n");
+    servo.ScanForServos();
+    pc.printf("\nDone.");
+    while(1);
+}
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
\ No newline at end of file
diff -r 000000000000 -r 9f7b70e0186e robotarm.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/robotarm.h	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,146 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ * 
+ * Robot Arm Controller 
+ *
+ * File: robotarm.h
+ *
+ * (C) Dept. Electronics & Computer Science, University of York
+ * 
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ * February 2017, Version 1.0
+ */ 
+
+
+#ifndef ROBOTARM_H
+#define ROBOTARM_H
+
+#define SOFTWARE_VERSION_CODE 1.0
+#define LCD_ADDRESS 0x7C
+
+// Remote control definitions:
+// REMOTE_ENABLED = 1 to enable remote control polling
+// REMOTE_POLL_RATE = poll rate in microseconds
+// REMOTE_LINEAR_STEPS = 1 to use same step amount each step; 0 to use a exponentially increasing amount as button held
+// REMOTE_START_SPEED = Size of step to use; suggest 2 for LINEAR_STEPS=0 or 10 for LINEAR_STEPS=1
+// REMOTE_USE_CURRENT_POSITION = 0 will move based on the stored target position, 1 will read the current position and set target accordingly.  This may make certain settings combinations fail to move under cases of high-torque load and low step size
+#define REMOTE_ENABLED 1
+#define REMOTE_POLL_RATE 15627
+#define REMOTE_LINEAR_STEPS 1
+#define REMOTE_START_SPEED 10
+#define REMOTE_USE_CURRENT_POSITION 0
+
+#define HAS_BASE 1
+#define HAS_SHOULDER 1
+#define HAS_ELBOW 1
+#define HAS_WRIST 1
+
+#define INVERT_BASE 1
+#define INVERT_SHOULDER 1
+#define INVERT_ELBOW 0
+#define INVERT_WRIST 0
+
+// Define the zero (middle\default) positions for the servos
+#define BASE_ZERO 2048
+#define SHOULDER_ZERO 2048
+#define ELBOW_ZERO 2048
+#define WRIST_ZERO 512
+
+// If USE_SOFT_LIMITS = 1, SERVOS WILL BE LIMITED TO RANGES THAT FOLLOW
+// We can also program the servos so they do not go outside limits.....
+#define USE_SOFT_LIMITS 1
+#define BASE_LIMIT_LOW 100
+#define BASE_LIMIT_HIGH 4000
+#define SHOULDER_LIMIT_LOW 700
+#define SHOULDER_LIMIT_HIGH 3400
+#define ELBOW_LIMIT_LOW 380
+#define ELBOW_LIMIT_HIGH 3720
+#define WRIST_LIMIT_LOW 0
+#define WRIST_LIMIT_HIGH 1023
+#define USE_LIMIT_WARNING 1
+
+#define BASE 10
+#define SHOULDER 11
+#define ELBOW 12
+#define WRIST 13
+
+#include "mbed.h"
+#include "display.h"
+#include "remote.h"
+#include "servo.h"
+#include "SerialHalfDuplex.h"
+
+extern Display display;
+extern Remote remote;
+extern Serial pc;
+extern Serial servo_data;
+extern Servo servo;
+
+extern char remote_on;
+extern short target_base;
+extern short target_shoulder;
+extern short target_elbow;
+extern short target_wrist;
+
+/** Robotarm Class
+ * The main class to define the robot arm
+ *
+ * Example code for main.cpp:
+ * @code
+ * #include "robotarm.h"
+ * Robotarm arm;
+ * int main(){
+ *    arm.init();
+ *    while(1) { //Do something!
+ *    }
+ * }
+ * @endcode
+ */
+class Robotarm
+{
+public:
+    /**
+     * Main initialisation routine for the robot arm
+     *
+     * Set up the display, set up listener for remote control, set up servos
+     */
+    void init(void);
+    
+    /**
+     * Test and initialise the servo motors
+     */
+    void init_servos(void);
+
+    /**
+     * Zero the servo motors (set to center position and activate)
+     * @param time_delay: The time delay in seconds to wait before zeroing (0=no delay)
+     */
+    void zero_servos(float time_delay);
+    
+    /**
+     * Called when servo initialisation fails; displays warning and halts program.
+     */
+    void fail_init(void);
+    
+    /**
+     * Hardware test scans for all servos at different baud rates
+     */
+    void hardware_test(void); 
+    
+    private:
+    
+    void _zero_servos(void);
+    void _zero_servos_display_update(void);
+};
+#endif // ROBOTARM_H
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
\ No newline at end of file
diff -r 000000000000 -r 9f7b70e0186e servo.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servo.cpp	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,963 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ *
+ * Dynamixel Servo Library for AX-12 and MX-28
+ *
+ * Based on library by Chris Styles (see copyright notice at end of file)
+ *
+ * File: servo.cpp
+ *
+ * (C) Dept. Electronics & Computer Science, University of York
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ * February 2017, Version 1.0
+ */
+
+#include "robotarm.h"
+
+int delay = RETURN_DELAY;
+char read_timeout_counter = 0;
+Servo::Servo(PinName tx, PinName rx)
+    : _servo(tx,rx)
+{
+    _servo.baud(57600);
+}
+
+void Servo::ClearBuffer()
+{
+    if (_servo.readable()) {
+        pc.printf("\nBuffer error:");
+        while(_servo.readable()) {
+            pc.printf("%c",_servo.getc());
+        }
+        pc.printf("\n");
+    }
+}
+void Servo::ScanForServos ()
+{
+    pc.printf("SCANNING FOR ServoS...\n");
+    pc.printf("Checking at 57600 baud\n");
+    _servo.baud(57600);
+    delay = 250;
+    for(int k=0; k<2; k++) {
+        if(k==1) {
+            _servo.baud(1000000);
+            pc.printf("\nChecking at 1000000 baud\n");
+        }
+        for(int id = 0; id<254; id++) {
+            //pc.printf("ID %d: ",id);
+            char TxBuf[8];
+            TxBuf[0] = 0xff;
+            TxBuf[1] = 0xff;
+            TxBuf[2] = id;
+            char sum = id + 7;
+            TxBuf[3] = 4;
+            TxBuf[4] = 2;
+            TxBuf[5] = REG_MODEL_NUMBER;
+            TxBuf[6] = 1;
+            TxBuf[7] = 0xFF - sum;
+            for (int i = 0; i<8 ; i++) {
+                _servo.putc(TxBuf[i]);
+            }
+            // Wait for data to transmit
+            int t_delay = 60;
+            wait_us(t_delay);
+            if(_servo.readable()) {
+                pc.printf("ID %d: ",id);
+                // Receive the Status packet 6+ number of bytes read
+                char status[8];
+                for (int i=0; i<(7) ; i++) {
+                    status[i] = _servo.getc();
+                }
+                if(status[2] == id) {
+                    pc.printf(" FOUND [");
+                    char modelnumber = status[5];
+                    switch(modelnumber) {
+                        case (AX12_MODEL):
+                            pc.printf("AX12]\n");
+                            break;
+                        case (MX28_MODEL):
+                            pc.printf("MX28]\n");
+                            break;
+                        default:
+                            pc.printf("UNKNOWN MODEL]\n");
+                            break;
+                    }
+                } else pc.printf(" ID ERROR\n");
+            } else {
+                //pc.printf(" NOT FOUND\n");
+            }
+        }
+    }
+    pc.printf("\nScan complete.\n");
+    delay = RETURN_DELAY;
+}
+
+// Get the soft lower limit for servo
+short Servo::GetLowerLimit(int ID)
+{
+    if(USE_SOFT_LIMITS==1){
+        switch(ID){
+          case BASE: return BASE_LIMIT_LOW;
+          case SHOULDER: return SHOULDER_LIMIT_LOW;
+          case ELBOW: return ELBOW_LIMIT_LOW;
+          case WRIST: return WRIST_LIMIT_LOW;   
+        }
+    }
+    return 0;
+}
+
+// Get the soft upper limit for servo   
+short Servo::GetUpperLimit(int ID)
+{
+    if(USE_SOFT_LIMITS==1){
+        switch(ID){
+          case BASE: return BASE_LIMIT_HIGH;
+          case SHOULDER: return SHOULDER_LIMIT_HIGH;
+          case ELBOW: return ELBOW_LIMIT_HIGH;
+          case WRIST: return WRIST_LIMIT_HIGH;   
+        }
+    }
+    if (ID == WRIST) return 1023;
+    return 4095;   
+}
+
+// Get detailed data for servo
+void Servo::DebugData(int ID)
+{
+    pc.printf("\nGetting Current Data for Servo %d",ID);
+
+
+    char data[49];
+    for(int i=0; i<12; i++) {
+        int offset = i*4;
+        int ErrorCode = read(ID, offset, 4, data+offset);
+        pc.printf(".");
+    }
+    pc.printf("\n");
+
+
+    pc.printf("\nEEPROM VALUES\n");
+
+    int modelnumber = data[0] + (data[1] << 8);
+    pc.printf("Model Number      : %x [",modelnumber);
+    switch(modelnumber) {
+        case (AX12_MODEL):
+            pc.printf("AX12]\n");
+            break;
+        case (MX28_MODEL):
+            pc.printf("MX28]\n");
+            break;
+        default:
+            pc.printf("UNKNOWN]\n");
+            break;
+    }
+    pc.printf("Firmware Version  : %x\n",data[2]);
+    pc.printf("ID                : %x\n",data[3]);
+    int baudrate = 2000000 / (data[4] + 1);
+    //Special high-speed baudrates [for MX28 only]
+    if(data[4] == 250) baudrate = 2250000;
+    if(data[4] == 251) baudrate = 2500000;
+    if(data[4] == 252) baudrate = 3000000;
+    pc.printf("Baud Rate         : %x [%d]\n",data[4],baudrate);
+    pc.printf("Return Delay Time : %x [%duS]\n",data[5],(data[5] * 2));
+    short cw_angle_limit = data[6] + (data[7] << 8);
+    short ccw_angle_limit = data[8] + (data[9] << 8);
+    pc.printf("CW Angle Limit    : %x [%d",cw_angle_limit,cw_angle_limit);
+    if(cw_angle_limit ==0 && ccw_angle_limit == 0)pc.printf(" - Wheel Mode]\n");
+    else {
+        if(cw_angle_limit == 4095 && ccw_angle_limit == 4095)pc.printf(" - Multiturn Mode]\n");
+        else pc.printf("- Joint Mode]\n");
+    }
+    pc.printf("CCW Angle Limit   : %x [%d",ccw_angle_limit,ccw_angle_limit);
+    if(cw_angle_limit ==0 && ccw_angle_limit == 0)pc.printf(" - Wheel Mode]\n");
+    else {
+        if(cw_angle_limit == 4095 && ccw_angle_limit == 4095)pc.printf(" - Multiturn Mode]\n");
+        else pc.printf("- Joint Mode]\n");
+    }
+    //Fill in blanks
+    pc.printf("High Temp Limit   : %x [%dC]\n",data[11],data[11]);
+    pc.printf("Low Voltage Limit : %x [%2.1fV]\n",data[12],(float) (data[12]*0.1f));
+    pc.printf("High Voltage Limit: %x [%2.1fV]\n",data[13],(float) (data[13]*0.1f));
+    short max_torque = data[14] + (data[15] << 8);
+    float pct_max_torque = (float) (max_torque / 10.23f);
+    pc.printf("Preset Max Torque : %x [%3.2f%%]\n",max_torque,pct_max_torque);
+    pc.printf("Status Return Lev.: %x [%d]\n",data[16]);
+    pc.printf("Alarm LED         : %x [%d]\n",data[17]);
+    pc.printf("Alarm Shutdown    : %x [%d]\n",data[18]);
+    short multiturn_offset = data[20] + (data[21] << 8);
+    pc.printf("Multiturn Offset  : %x [%d]\n",multiturn_offset,multiturn_offset);
+    pc.printf("\nRAM VALUES\n");
+    pc.printf("Torque Enable     : %x\n",data[24]);
+    pc.printf("LED               : %x\n",data[25]);
+    pc.printf("D Gain            : %x [%d]\n",data[26],data[26]);
+    pc.printf("I Gain            : %x [%d]\n",data[27],data[27]);
+    pc.printf("P Gain            : %x [%d]\n",data[28],data[28]);
+    short goal_position = data[30] + (data[31] << 8);
+    float gp_degrees = (goal_position - 2048) * 0.087890625;
+    pc.printf("Goal Position     : %x [%d: %3.2f degrees]\n",goal_position,goal_position,gp_degrees);
+    short moving_speed = data[32] + (data[33] << 8);
+    float mv_rpm = moving_speed * 0.114;
+    pc.printf("Moving Speed      : %x [%d: %4.2 rpm]\n",moving_speed,moving_speed,mv_rpm);
+    short c_max_torque = data[34] + (data[35] << 8);
+    float cpct_max_torque = (float) (c_max_torque / 10.23f);
+    pc.printf("Current Max Torque: %x [%3.2f%%]\n",c_max_torque,cpct_max_torque);
+    short present_position = data[36] + (data[37] << 8);
+    float pp_degrees = present_position * 0.088f;
+    pc.printf("Present Position  : %x [%d: %3.2f degrees]\n",present_position,present_position,pp_degrees);
+    short present_speed = data[38] + (data[39] << 8);
+    float p_rpm = present_speed * 0.114;
+    pc.printf("Present Speed     : %x [%d: %4.2 rpm]\n",present_speed,present_speed,p_rpm);
+    short present_load = data[40] + (data[41] << 8);
+    if(present_load < 1024) {
+        float present_loadpct = (1024 - present_load) / 10.23f;
+        pc.printf("Present Load      : %x [%3.2f%% CCW]\n",present_load,present_loadpct);
+    } else {
+        if(present_load > 1024) {
+            float present_loadpct_cw = (present_load - 1024) / 10.23f;
+            pc.printf("Present Load      : %x [%3.2f%% CW]\n",present_load,present_loadpct_cw);
+        } else  pc.printf("Present Load      : %x [NONE]\n",present_load);
+    }
+    pc.printf("Voltage           : %x [%fV]\n",data[42],(data[42] * 0.1f));
+    pc.printf("Temperature       : %x [%dC]\n",data[43],data[43]);
+
+
+
+
+}
+
+// Set the mode of the servo
+//  0 = Positional (0-300 degrees)
+//  1 = Rotational -1 to 1 speed
+int Servo::SetMode(int ID, int mode)
+{
+
+    if (mode == 1) { // set CR
+        SetCWLimit(ID, 0);
+        SetCCWLimit(ID, 0);
+        SetCRSpeed(ID, 0.0);
+    } else {
+        SetCWLimit(ID, 0);
+        SetCCWLimit(ID, 300);
+        SetCRSpeed(ID, 0.0);
+    }
+    return(0);
+}
+
+// if flag[0] is set, were blocking
+// if flag[1] is set, we're registering
+// they are mutually exclusive operations
+int Servo::SetGoal(int ID, short goal, int flags)
+{
+
+    char reg_flag = 0;
+    char data[2];
+
+    // set the flag is only the register bit is set in the flag
+    if (flags == 0x2) {
+        reg_flag = 1;
+    }
+    if(GetLowerLimit(ID) > goal){
+       goal=GetLowerLimit(ID);
+       if(USE_LIMIT_WARNING == 1){
+            display.clear_display();
+            display.set_position(0,0);
+            display.write_string("RANGE ERROR");
+       }   
+    }
+    if(GetUpperLimit(ID) < goal){
+       goal=GetUpperLimit(ID);
+       if(USE_LIMIT_WARNING == 1){
+            display.clear_display();
+            display.set_position(0,0);
+            display.write_string("RANGE ERROR");
+       }   
+    }
+    if (DEBUG) {
+        pc.printf("SetGoal to 0x%x ",goal);
+    }
+
+    // Apply inversions if set
+    switch(ID){
+       case(BASE):if(INVERT_BASE == 1)goal=4095-goal;break;
+       case(SHOULDER):if(INVERT_SHOULDER == 1)goal=4095-goal;break;
+       case(ELBOW):if(INVERT_ELBOW == 1)goal=4095-goal;break;
+       case(WRIST):if(INVERT_WRIST == 1)goal=1023-goal;break;
+   
+    }
+    
+    data[0] = goal & 0xff; // bottom 8 bits
+    data[1] = goal >> 8;   // top 8 bits
+
+    // write the packet, return the error code
+    int rVal = write(ID, REG_GOAL_POSITION, 2, data, reg_flag);
+
+    if (flags == 1) {
+        // block until it comes to a halt
+        if (DEBUG) pc.printf(" [WAITING]");
+        while (isMoving(ID)) {}
+    }
+    if (DEBUG) pc.printf("\n");
+    return(rVal);
+}
+
+// if flag[0] is set, were blocking
+// if flag[1] is set, we're registering
+// they are mutually exclusive operations
+int Servo::SetGoalDegrees(int ID, int degrees, int flags)
+{
+    short goal = (degrees * 11.377778) + 2048;
+    return SetGoal(ID,goal,flags);
+}
+
+
+// Set continuous rotation speed from -1 to 1
+int Servo::SetCRSpeed(int ID, float speed)
+{
+
+    // bit 10     = direction, 0 = CCW, 1=CW
+    // bits 9-0   = Speed
+    char data[2];
+
+    int goal = (0x3ff * abs(speed));
+
+    // Set direction CW if we have a negative speed
+    if (speed < 0) {
+        goal |= (0x1 << 10);
+    }
+
+    data[0] = goal & 0xff; // bottom 8 bits
+    data[1] = goal >> 8;   // top 8 bits
+
+    // write the packet, return the error code
+    int rVal = write(ID, 0x20, 2, data);
+
+    return(rVal);
+}
+
+
+int Servo::SetCWLimit (int ID, int degrees)
+{
+
+    char data[2];
+
+    // 1023 / 300 * degrees
+    short limit = (1023 * degrees) / 300;
+
+    if (DEBUG) {
+        pc.printf("SetCWLimit to 0x%x\n",limit);
+    }
+
+    data[0] = limit & 0xff; // bottom 8 bits
+    data[1] = limit >> 8;   // top 8 bits
+
+    // write the packet, return the error code
+    return (write(ID, REG_CW_LIMIT, 2, data));
+
+}
+
+int Servo::SetCCWLimit (int ID, int degrees)
+{
+
+    char data[2];
+
+    // 1023 / 300 * degrees
+    short limit = (1023 * degrees) / 300;
+
+    if (DEBUG) {
+        pc.printf("SetCCWLimit to 0x%x\n",limit);
+    }
+
+    data[0] = limit & 0xff; // bottom 8 bits
+    data[1] = limit >> 8;   // top 8 bits
+
+    // write the packet, return the error code
+    return (write(ID, REG_CCW_LIMIT, 2, data));
+}
+
+int Servo::SetTorqueEnable (int ID, int enable)
+{
+    char data[1];
+    data[0]=enable;
+    if (DEBUG) {
+        pc.printf("SetTorqueEnable to %d\n",enable);
+    }
+
+
+    // write the packet, return the error code
+    return (write(ID, REG_TORQUE_ENABLE, 1, data));
+}
+
+int Servo::SetLowVoltageLimit (int ID, char lv_limit)
+{
+
+    char data[1];
+    data[0] = lv_limit;
+    if (DEBUG) {
+        pc.printf("Setting low voltage limit to %2.1f\n",(float) lv_limit / 10.0);
+    }
+    return (write(ID, REG_LOW_VOLTAGE_LIMIT, 1, data));
+}
+
+int Servo::LockEeprom (int ID)
+{
+    char data[1];
+    data[0]=1;
+    if (DEBUG) {
+        pc.printf("Locking EEPROM\n");
+    }
+    return (write(ID, REG_EEPROM_LOCK, 1, data));
+}
+
+int Servo::SetHighVoltageLimit (int ID, char hv_limit)
+{
+
+    char data[1];
+    data[0] = hv_limit;
+    if (DEBUG) {
+        pc.printf("Setting high voltage limit to %2.1f\n",(float) hv_limit / 10.0);
+    }
+    return (write(ID, REG_HIGH_VOLTAGE_LIMIT, 1, data));
+}
+
+int Servo::SetDelayTime (int ID, char delay)
+{
+    char data[1];
+    data[0] = delay;
+    if (DEBUG) {
+        pc.printf("Setting delay time to %dus\n",delay+delay);
+    }
+    return (write(ID, REG_RETURN_DELAY, 1, data));
+}
+
+
+
+int Servo::SetTemperatureLimit (int ID, char temp_limit)
+{
+
+    char data[1];
+    data[0] = temp_limit;
+    if (DEBUG) {
+        pc.printf("Setting temperature limit to %dC\n",temp_limit);
+    }
+    return (write(ID, REG_HIGHTEMP_LIMIT, 1, data));
+}
+
+int Servo::SetID (int CurrentID, int NewID)
+{
+
+    char data[1];
+    data[0] = NewID;
+    if (DEBUG) {
+        pc.printf("Setting ID from 0x%x to 0x%x\n",CurrentID,NewID);
+    }
+    return (write(CurrentID, REG_ID, 1, data));
+
+}
+
+int Servo::SetBaud (int ID, int baud)
+{
+
+    char data[1];
+    data[0] = baud;
+    if (DEBUG) {
+        pc.printf("Setting baud to %d\n",(2000000 / baud));
+    }
+    return (write(ID, REG_BAUDRATE, 1, data));
+
+}
+
+
+// return 1 is the servo is still in flight
+int Servo::isMoving(int ID)
+{
+
+    char data[1];
+    read(ID,REG_MOVING,1,data);
+    return(data[0]);
+}
+
+
+void Servo::trigger(void)
+{
+
+    char TxBuf[16];
+    char sum = 0;
+
+    if (TRIGGER_DEBUG) {
+        pc.printf("\nTriggered\n");
+    }
+
+    // Build the TxPacket first in RAM, then we'll send in one go
+    if (TRIGGER_DEBUG) {
+        pc.printf("\nTrigger Packet\n  Header : 0xFF, 0xFF\n");
+    }
+
+    TxBuf[0] = 0xFF;
+    TxBuf[1] = 0xFF;
+
+    // ID - Broadcast
+    TxBuf[2] = 0xFE;
+    sum += TxBuf[2];
+
+    if (TRIGGER_DEBUG) {
+        pc.printf("  ID : %d\n",TxBuf[2]);
+    }
+
+    // Length
+    TxBuf[3] = 0x02;
+    sum += TxBuf[3];
+    if (TRIGGER_DEBUG) {
+        pc.printf("  Length %d\n",TxBuf[3]);
+    }
+
+    // Instruction - ACTION
+    TxBuf[4] = 0x04;
+    sum += TxBuf[4];
+    if (TRIGGER_DEBUG) {
+        pc.printf("  Instruction 0x%X\n",TxBuf[5]);
+    }
+
+    // Checksum
+    TxBuf[5] = 0xFF - sum;
+    if (TRIGGER_DEBUG) {
+        pc.printf("  Checksum 0x%X\n",TxBuf[5]);
+    }
+
+    // Transmit the packet in one burst with no pausing
+    for (int i = 0; i < 6 ; i++) {
+        _servo.putc(TxBuf[i]);
+    }
+
+    // This is a broadcast packet, so there will be no reply
+
+    return;
+}
+
+int Servo::GetModelNumber(int ID)
+{
+    if (DEBUG) {
+        pc.printf("\nGetModelNumber(%d)",ID);
+    }
+    char data[2];
+    int ErrorCode = read(ID, REG_MODEL_NUMBER, 2, data);
+    int modelnumber = data[0] + (data[1] << 8);
+    return (modelnumber);
+}
+
+float Servo::GetPositionDegrees(int ID)
+{
+    short position = GetPosition(ID);
+    //float angle = (position * 300)/1024;  FOR AX-12
+    float angle = (position - 2048) * 0.087890625;
+
+    return (angle);
+}
+
+short Servo::GetPosition(int ID)
+{
+
+    if (DEBUG) {
+        pc.printf("\nGetPosition(%d)",ID);
+    }
+
+    char data[2];
+
+    int ErrorCode = read(ID, REG_POSITION, 2, data);
+    if (DEBUG) {
+        pc.printf("[EC=%d]",ErrorCode);
+    }
+    short position = data[0] + (data[1] << 8);
+    
+    // Apply inversions if set
+    switch(ID){
+       case(BASE):if(INVERT_BASE == 1)position=4095-position;break;
+       case(SHOULDER):if(INVERT_SHOULDER == 1)position=4095-position;break;
+       case(ELBOW):if(INVERT_ELBOW == 1)position=4095-position;break;
+       case(WRIST):if(INVERT_BASE == 1)position=4095-position;break;
+   
+    }
+    return (position);
+}
+
+
+float Servo::GetTemp (int ID)
+{
+
+    if (DEBUG) {
+        pc.printf("\nGetTemp(%d)",ID);
+    }
+    char data[1];
+    int ErrorCode = read(ID, REG_TEMP, 1, data);
+    float temp = data[0];
+    return(temp);
+}
+
+short Servo::GetTemperature(int ID)
+{
+    if (DEBUG) {
+        pc.printf("\nGetTemperature(%d)",ID);
+    }
+    char data[1];
+    int ErrorCode = read(ID, REG_TEMP, 1, data);
+    return (short) (data[0]);
+}
+
+float Servo::GetVolts (int ID)
+{
+    if (DEBUG) {
+        pc.printf("\nGetVolts(%d)",ID);
+    }
+    char data[1];
+    int ErrorCode = read(ID, REG_VOLTS, 1, data);
+    float volts = data[0]/10.0;
+    return(volts);
+}
+
+short Servo::GetVoltage(int ID)
+{
+    if (DEBUG) {
+        pc.printf("\nGetVoltage(%d)",ID);
+    }
+    char data[1];
+    int ErrorCode = read(ID, REG_VOLTS, 1, data);
+    return (short) (data[0]);
+}
+
+short Servo::GetLoad(int ID)
+{
+    if (DEBUG) {
+        pc.printf("\nGetLoad(%d)",ID);
+    }
+    char data[2];
+    int ErrorCode = read(ID, REG_LOAD, 2, data);
+    return (short) (data[0] + (data[1]<<8));
+}
+
+short Servo::GetSpeed(int ID)
+{
+    if (DEBUG) {
+        pc.printf("\nGetSpeed(%d)",ID);
+    }
+    char data[2];
+    int ErrorCode = read(ID, REG_SPEED, 2, data);
+    return (short) (data[0] + (data[1]<<8));
+}
+
+int Servo::read(int ID, int start, int bytes, char* data)
+{
+
+    char PacketLength = 0x4;
+    char TxBuf[16];
+    char sum = 0;
+    char Status[16];
+
+    Status[4] = 0xFE; // return code
+
+    if (READ_DEBUG) {
+        pc.printf("\nread(%d,0x%x,%d,data)\n",ID,start,bytes);
+    }
+
+    // Build the TxPacket first in RAM, then we'll send in one go
+    if (READ_DEBUG) {
+        pc.printf("\nInstruction Packet\n  Header : 0xFF, 0xFF\n");
+    }
+
+    TxBuf[0] = 0xff;
+    TxBuf[1] = 0xff;
+
+    // ID
+    TxBuf[2] = ID;
+    sum += TxBuf[2];
+    if (READ_DEBUG) {
+        pc.printf("  ID : %d\n",TxBuf[2]);
+    }
+
+    // Packet Length
+    TxBuf[3] = PacketLength;    // Length = 4 ; 2 + 1 (start) = 1 (bytes)
+    sum += TxBuf[3];            // Accululate the packet sum
+    if (READ_DEBUG) {
+        pc.printf("  Length : 0x%x\n",TxBuf[3]);
+    }
+
+    // Instruction - Read
+    TxBuf[4] = 0x2;
+    sum += TxBuf[4];
+    if (READ_DEBUG) {
+        pc.printf("  Instruction : 0x%x\n",TxBuf[4]);
+    }
+
+    // Start Address
+    TxBuf[5] = start;
+    sum += TxBuf[5];
+    if (READ_DEBUG) {
+        pc.printf("  Start Address : 0x%x\n",TxBuf[5]);
+    }
+
+    // Bytes to read
+    TxBuf[6] = bytes;
+    sum += TxBuf[6];
+    if (READ_DEBUG) {
+        pc.printf("  No bytes : 0x%x\n",TxBuf[6]);
+    }
+
+    // Checksum
+    TxBuf[7] = 0xFF - sum;
+    if (READ_DEBUG) {
+        pc.printf("  Checksum : 0x%x\n",TxBuf[7]);
+    }
+
+    // Transmit the packet in one burst with no pausing
+    for (int i = 0; i<8 ; i++) {
+        _servo.putc(TxBuf[i]);
+    }
+
+    // Wait for data to transmit
+    wait_us(60); //was 60
+
+
+    // Skip if the read was to the broadcast address
+    if (ID != 0xFE) {
+        int timedout = 0;
+        int timeout_count = 0;
+        while(!_servo.readable()) {
+            timeout_count++;
+            if(timeout_count % 10000 == 0) {
+                timedout=1;
+                break;
+            }
+        }
+        if(timedout==1) {
+            read_timeout_counter++;
+            if(DEBUG)pc.printf("  Read timed out [%d of %d]\n",read_timeout_counter,READ_TIMEOUT_LIMIT);
+            if(read_timeout_counter == READ_TIMEOUT_LIMIT){
+                display.clear_display();
+                display.set_position(0,0);
+                display.write_string("SERVO ERROR");
+                read_timeout_counter = 0;
+                return 255;
+            }
+            return read(ID,start,bytes,data);
+        } else {
+            read_timeout_counter = 0;
+            // Receive the Status packet 6+ number of bytes read
+            for (int i=0; i<(6+bytes) ; i++) {
+                Status[i] = _servo.getc();
+            }
+
+            // Copy the data from Status into data for return
+            for (int i=0; i < Status[3]-2 ; i++) {
+                data[i] = Status[5+i];
+            }
+
+            if (READ_DEBUG) {
+                pc.printf("\nStatus Packet\n");
+                pc.printf("  Header : 0x%x\n",Status[0]);
+                pc.printf("  Header : 0x%x\n",Status[1]);
+                pc.printf("  ID : 0x%x\n",Status[2]);
+                pc.printf("  Length : 0x%x\n",Status[3]);
+                pc.printf("  Error Code : 0x%x\n",Status[4]);
+
+                for (int i=0; i < Status[3]-2 ; i++) {
+                    pc.printf("  Data : 0x%x\n",Status[5+i]);
+                }
+
+                pc.printf("  Checksum : 0x%x\n",Status[5+(Status[3]-2)]);
+            }
+
+        } // if (ID!=0xFE)
+        wait_us(5);
+    }
+    return(Status[4]);
+}
+
+
+int Servo:: write(int ID, int start, int bytes, char* data, int flag)
+{
+// 0xff, 0xff, ID, Length, Intruction(write), Address, Param(s), Checksum
+
+    char TxBuf[16];
+    char sum = 0;
+    char Status[6];
+
+    if (WRITE_DEBUG) {
+        pc.printf("\nwrite(%d,0x%x,%d,data,%d)\n",ID,start,bytes,flag);
+    }
+
+    // Build the TxPacket first in RAM, then we'll send in one go
+    if (WRITE_DEBUG) {
+        pc.printf("\nInstruction Packet\n  Header : 0xFF, 0xFF\n");
+    }
+
+    TxBuf[0] = 0xff;
+    TxBuf[1] = 0xff;
+
+    // ID
+    TxBuf[2] = ID;
+    sum += TxBuf[2];
+
+    if (WRITE_DEBUG) {
+        pc.printf("  ID : %d\n",TxBuf[2]);
+    }
+
+    // packet Length
+    TxBuf[3] = 3+bytes;
+    sum += TxBuf[3];
+
+    if (WRITE_DEBUG) {
+        pc.printf("  Length : %d\n",TxBuf[3]);
+    }
+
+    // Instruction
+    if (flag == 1) {
+        TxBuf[4]=0x04;
+        sum += TxBuf[4];
+    } else {
+        TxBuf[4]=0x03;
+        sum += TxBuf[4];
+    }
+
+    if (WRITE_DEBUG) {
+        pc.printf("  Instruction : 0x%x\n",TxBuf[4]);
+    }
+
+    // Start Address
+    TxBuf[5] = start;
+    sum += TxBuf[5];
+    if (WRITE_DEBUG) {
+        pc.printf("  Start : 0x%x\n",TxBuf[5]);
+    }
+
+    // data
+    for (char i=0; i<bytes ; i++) {
+        TxBuf[6+i] = data[i];
+        sum += TxBuf[6+i];
+        if (WRITE_DEBUG) {
+            pc.printf("  Data : 0x%x\n",TxBuf[6+i]);
+        }
+    }
+
+    // checksum
+    TxBuf[6+bytes] = 0xFF - sum;
+    if (WRITE_DEBUG) {
+        pc.printf("  Checksum : 0x%x\n",TxBuf[6+bytes]);
+    }
+
+    // Transmit the packet in one burst with no pausing
+    for (int i = 0; i < (7 + bytes) ; i++) {
+        _servo.putc(TxBuf[i]);
+    }
+
+    // Wait for data to transmit
+    wait_us(60);
+
+    // make sure we have a valid return
+    Status[4]=0x00;
+
+    // we'll only get a reply if it was not broadcast
+    if (ID!=0xFE) {
+        int timedout = 0;
+        int timeout_count = 0;
+        while(!_servo.readable()) {
+            timeout_count++;
+            if(timeout_count % 10000 == 0) {
+                timedout=1;
+                break;
+            }
+        }
+        if(timedout==1) {
+            read_timeout_counter++;
+            if(DEBUG)pc.printf("  Write ack. timed out [%d of %d]\n",read_timeout_counter,READ_TIMEOUT_LIMIT);
+            if(read_timeout_counter == READ_TIMEOUT_LIMIT){
+                display.clear_display();
+                display.set_position(0,0);
+                display.write_string("SERVO ERROR");
+                read_timeout_counter = 0;
+                return 255;
+            }
+            return write(ID,start,bytes,data,flag);
+        } else {
+            read_timeout_counter = 0;
+            // response is always 6 bytes
+            // 0xFF, 0xFF, ID, Length Error, Param(s) Checksum
+            for (int i=0; i < 6 ; i++) {
+                Status[i] = _servo.getc();
+            }
+        }
+        // Build the TxPacket first in RAM, then we'll send in one go
+        if (WRITE_DEBUG) {
+            pc.printf("\nStatus Packet\n  Header : 0x%X, 0x%X\n",Status[0],Status[1]);
+            pc.printf("  ID : %d\n",Status[2]);
+            pc.printf("  Length : %d\n",Status[3]);
+            pc.printf("  Error : 0x%x\n",Status[4]);
+            pc.printf("  Checksum : 0x%x\n",Status[5]);
+        }
+
+
+    }
+
+    return(Status[4]); // return error code
+}
+
+//Set the baud rate for serial connection to something other than default(1000000)
+void Servo::SetInitBaud(int baud, int delaytime)
+{
+    pc.printf("Setting serial baud rate to %d\n",baud);
+    _servo.baud(baud);
+    delay = delaytime;
+}
+
+/* Additional copyright notice */
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
+
+/*
+ * Copyright (c) 2010, Chris Styles (http://mbed.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * Copyright (c) 2010, Chris Styles (http://mbed.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
diff -r 000000000000 -r 9f7b70e0186e servo.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servo.h	Thu Feb 16 23:57:45 2017 +0000
@@ -0,0 +1,372 @@
+/* University of York Robotics Laboratory Robot Arm Controller Board
+ *
+ * Dynamixel Servo Library for AX-12 and MX-28
+ *
+ * Based on library by Chris Styles (see copyright notice at end of file)
+ *
+ * File: servo.cpp
+ *
+ * (C) Dept. Electronics & Computer Science, University of York
+ * James Hilder, Alan Millard, Shuhei Miyashita, Homero Elizondo, Jon Timmis
+ *
+ * February 2017, Version 1.0
+ */
+ 
+// NOTE:
+// When communicating with 'new' servos the are defaulted to ID 1
+// Make sure only one such servo is connected at one time
+// Also note that this library defaults to 57600 serial comms
+// The MX-28s are defaulted to communicate at this speed but the AX-12s are not (1Mbaud)
+// Use SetInitBaud(57600) to override the default and then change baud rate when setting up MX-28s
+
+#ifndef SERVO_H
+#define SERVO_H
+
+#include "mbed.h"
+#include "SerialHalfDuplex.h"
+
+#define WRITE_DEBUG 0
+#define READ_DEBUG 0
+#define TRIGGER_DEBUG 0
+#define DEBUG 0
+
+// Number of times to retry a read\write operation if read times out
+#define READ_TIMEOUT_LIMIT 3
+
+#define LOW_VOLTAGE_LIMIT 90
+#define HIGH_VOLTAGE_LIMIT 140
+#define HIGH_TEMPERATURE_LIMIT 70
+
+#define AX12_MODEL 0x0C
+#define MX28_MODEL 0x1D
+
+#define RETURN_DELAY 250
+
+#define REG_MODEL_NUMBER 0x00
+#define REG_FIRMWARE_VERSION 0x02
+#define REG_ID 0x03
+#define REG_BAUDRATE 0x04
+#define REG_RETURN_DELAY 0x05
+#define REG_CW_LIMIT 0x06
+#define REG_CCW_LIMIT 0x08
+#define REG_HIGHTEMP_LIMIT 0x0B
+#define REG_LOW_VOLTAGE_LIMIT 0x0C
+#define REG_HIGH_VOLTAGE_LIMIT 0x0D
+#define REG_MAX_TORQUE 0x0E
+#define REG_TORQUE_ENABLE 0x18
+#define REG_GOAL_POSITION 0x1E
+#define REG_MOVING_SPEED 0x20
+#define REG_VOLTS 0x2A
+#define REG_TEMP 0x2B
+#define REG_SPEED 0x26
+#define REG_LOAD 0x28
+#define REG_MOVING 0x2E
+#define REG_POSITION 0x24
+#define REG_EEPROM_LOCK 0x2F
+
+#define MODE_POSITION  0
+#define MODE_ROTATION  1
+
+#define CW 1
+#define CCW 0
+
+extern Serial data;
+
+/** Servo Class
+ * Class with functions to control a Dynamixel single-wire TTL Serial Servo
+ *
+ * Example code for main.cpp:
+ * @code
+ * #include "servo.h"
+ * Servo servo(p9,p10)
+ * int main(){
+ *    arm.init();
+ *    while(1) { //Do something!
+ *    }
+ * }
+ * @endcode
+ */
+class Servo {
+
+public:
+
+    /** Create a Dynamixel servo controller object connected to the specified serial port, with the specified ID
+     *
+     * @param pin tx pin
+     * @param pin rx pin 
+     */
+    Servo(PinName tx, PinName rx);
+
+    void ClearBuffer(void);
+
+    void ScanForServos(void);
+
+
+    /** Prints (over PC serial) a detailed set of data for the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     */
+    void DebugData(int ID);
+
+    /** Set the mode of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param mode
+     *    0 = Positional, default
+     *    1 = Continuous rotation
+     */
+    int SetMode(int ID,  int mode);
+
+    /** Set goal angle, in positional mode
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param goal 0-4095
+     * @param flags, defaults to 0
+     *    flags[0] = blocking, return when goal position reached 
+     *    flags[1] = register, activate with a broadcast trigger
+     *
+     */
+    int SetGoal(int ID, short goal, int flags = 0);
+
+    /** Set goal angle in integer degrees, in positional mode
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param degrees 0-300
+     * @param flags, defaults to 0
+     *    flags[0] = blocking, return when goal position reached 
+     *    flags[1] = register, activate with a broadcast trigger
+     *
+     */
+    int SetGoalDegrees(int ID, int degrees, int flags = 0);
+
+
+    /** Set the speed of the servo in continuous rotation mode
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param speed, -1.0 to 1.0
+     *   -1.0 = full speed counter clock wise
+     *    1.0 = full speed clock wise
+     */
+    int SetCRSpeed(int ID, float speed);
+
+
+    /** Set the clockwise limit of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param degrees, 0-300
+     */
+    int SetCWLimit(int ID, int degrees);
+    
+    /** Set the counter-clockwise limit of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param degrees, 0-300
+     */
+    int SetCCWLimit(int ID, int degrees);
+
+    /** Enable or disable the torque hold of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param enable, 0=disable (floppy) 1=enable (hold)
+     */
+     int SetTorqueEnable (int ID, int enable);
+
+
+    /** Change the low voltage limit of a servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param lv_limit, 0-254
+     *
+     * voltage = lv_limit / 10
+     */
+    int SetLowVoltageLimit (int ID, char lv_limit);
+    
+    /** Lock the EEPROM area of a servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     */
+    int LockEeprom(int ID);
+    
+    /** Change the high voltage limit of a servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param hv_limit, 0-254
+     *
+     * voltage = hv_limit / 10
+     */
+    int SetHighVoltageLimit (int ID, char hv_limit);
+    
+    /** Change the delay time of a servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param delay, 0-254 C
+     *
+     */
+    int SetDelayTime (int ID, char delay);
+    
+    /** Change the high temperature limit of a servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param temp_limit, 0-254 C
+     *
+     */
+    int SetTemperatureLimit (int ID, char temp_limit);
+    
+    /** Change the ID of a servo
+     *
+     * @param CurentID 1-255
+     * @param NewID 1-255
+     *
+     * If a servo ID is not know, the broadcast address of 0 can be used for CurrentID.
+     * In this situation, only one servo should be connected to the bus
+     */
+    int SetID(int CurrentID, int NewID);
+
+    /** Change the baud of a servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @param baud, 0-252
+     *
+     * baudrate = 2000000/baud (with special cases 250=2.25Mbps, 251=2.5Mbps and 252=3Mbps on MX-28 only)
+     */
+    int SetBaud (int ID, int baud);
+
+    /** Poll to see if the servo is moving
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns true is the servo is moving
+     */
+    int isMoving(int ID);
+
+    /** Send the broadcast "trigger" command, to activate any outstanding registered commands
+     */
+    void trigger(void);
+
+    /** Read the model number of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns int matching defined model number
+     */
+    int GetModelNumber(int ID);
+
+    /** Read the current angle of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns float in the range 0.0-300.0
+     */
+    float GetPositionDegrees(int ID);
+
+
+    /** Read the raw angle reading of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns short in the range 0 - 4095 for MX-28
+     */
+    short GetPosition(int ID);
+
+
+    /** Read the temperature of the servo
+     *
+     * @returns float temperature 
+     */
+    float GetTemp(int ID);
+
+    /** Read the supply voltage of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns short voltage * 10
+     */
+    short GetVoltage(int ID) ;
+    
+    /** Read the supply voltage of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns float voltage
+     */
+    float GetVolts(int ID);
+    
+    /** Read the temperature of the servo
+     *
+     * @returns short temperature 
+     */
+    short GetTemperature(int ID);
+
+    /** Read the torque load of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns short load, range 0-2048, 0 is 100% CCW, 1024 is zero and 2048 is 100% CW
+     */
+    short GetLoad(int ID) ;
+    
+    /** Read the speed of the servo
+     *
+     * @param ID, the Bus ID of the servo 1-255 
+     * @returns short speed
+     */
+    short GetSpeed(int ID) ;
+        
+     /** Change the internal baud rate to something other than 1000000 (note MX-28s default to 57600)
+     *
+     * @param baud, the baud rate to set
+     * @param delaytime, the delay time to set (x2 us)
+     */
+    void SetInitBaud(int baud, int delaytime);
+    
+    /** Returns the software lower limit for the given servo
+     *
+     * @param ID, the servo ID to return the limit for
+     * @returns short limit (either preset value or 0)
+     */
+    short GetLowerLimit(int ID);
+
+    /** Returns the software upper limit for the given servo
+     *
+     * @param ID, the servo ID to return the limit for
+     * @returns short limit (either preset value or 4095)
+     */
+    short GetUpperLimit(int ID);
+
+
+private :
+    SerialHalfDuplex _servo;
+    int read(int ID, int start, int length, char* data);
+    int write(int ID, int start, int length, char* data, int flag=0);
+
+};
+
+
+
+#endif
+
+/*
+ * Copyright 2017 University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
+
+/*
+ * Copyright (c) 2010, Chris Styles (http://mbed.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
\ No newline at end of file