For YRL Robot Arm

Revision:
0:b14dfd8816da
diff -r 000000000000 -r b14dfd8816da servo.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servo.cpp	Fri Mar 03 13:28:54 2017 +0000
@@ -0,0 +1,962 @@
+/* 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;   
+        }
+    }
+    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_BASE == 1)goal=4095-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.
+ */