For YRL Robot Arm
Diff: servo.cpp
- 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. + */