Robotis Dynamixel MX-12W Servo Library

Dependents:   SpindleBot_1_5b Utilisatio_MX12_V4

/media/uploads/labmrd/mx12.jpg

This is my attempt to adapt Chris Styles's AX12 library to work with my Dynamixel MX12 servos. This library is still very much a work in progress, and it may have some/many errors in it, but hopefully I will keep improving it to bring it up to snuff.

Dynamixel aficionados should also check out This MX28 library for a completely separate library that provides very similar functionality, and I wish I had known it existed before I started my work...

minimal example

#include "mbed.h"
#include "MX12.h"

int main() {

  MX12 mymx12 (p9, p10, 1);           // ID=1

  while (1) {
      mymx12.Set_Goal_Position(0);    // go to 0 degrees
      wait (2.0);
      mymx12.Set_Goal_Position(300);  // go to 300 degrees
      wait (2.0);
  }
}
Revision:
1:946a27210553
Parent:
0:29900c3a4a50
Child:
3:624d04c390b8
--- a/MX12.cpp	Tue Nov 25 03:07:36 2014 +0000
+++ b/MX12.cpp	Mon Jan 26 04:02:37 2015 +0000
@@ -1,274 +1,230 @@
-/* mbed AX-12+ Servo Library
- *
- * Copyright (c) 2010, cstyles (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:
+/* mbed MX-12 Servo Library
  *
- * 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.
  */
 
 #include "MX12.h"
 #include "mbed.h"
 
-#ifndef M_PI
-    #define M_PI    3.14159265358979323846  /* pi */
-#endif
-#ifndef M_PI_2
-    #define M_PI_2  1.57079632679489661923  /* pi/2 */
-#endif
+MX12OD_Object MX12_OD[MX12_OD_SIZE];
+bool MX12OD_Object_initalized;
+
+MX12::MX12(PinName tx, PinName rx, int ID, int baud_rate)
+        : mx12_out(tx, NC),
+          mx12_in(NC, rx),
+          profileOut(p12) {
+    mx12_out.baud(baud_rate);
+    mx12_in.baud(baud_rate);
+    _ID = ID;
+}
+
 
-MX12::MX12(PinName tx, PinName rx, int ID)
-        : _mx12(tx,rx) {
-
-    _mx12.baud(1000000);//57600);//1000000);
-    _ID = ID;
+void MX12::Init(void){
+    if(MX12OD_Object_initalized){
+        return;
+    }
+    MX12_OD[MX12_REG_MODEL_NUMBER                   ].Address=0;    MX12_OD[MX12_REG_MODEL_NUMBER                   ].Bytes=2;
+    MX12_OD[MX12_REG_VERSION_OF_FIRMWARE            ].Address=2;    MX12_OD[MX12_REG_VERSION_OF_FIRMWARE            ].Bytes=1;
+    MX12_OD[MX12_REG_ID                             ].Address=3;    MX12_OD[MX12_REG_ID                             ].Bytes=1;
+    MX12_OD[MX12_REG_BAUD_RATE                      ].Address=4;    MX12_OD[MX12_REG_BAUD_RATE                      ].Bytes=1;
+    MX12_OD[MX12_REG_RETURN_DELAY_TIME              ].Address=5;    MX12_OD[MX12_REG_RETURN_DELAY_TIME              ].Bytes=1;
+    MX12_OD[MX12_REG_CW_ANGLE_LIMIT                 ].Address=6;    MX12_OD[MX12_REG_CW_ANGLE_LIMIT                 ].Bytes=2;
+    MX12_OD[MX12_REG_CCW_ANGLE_LIMIT                ].Address=8;    MX12_OD[MX12_REG_CCW_ANGLE_LIMIT                ].Bytes=2;
+    MX12_OD[MX12_REG_THE_HIGHEST_LIMIT_TEMPERATURE  ].Address=11;   MX12_OD[MX12_REG_THE_HIGHEST_LIMIT_TEMPERATURE  ].Bytes=1;
+    MX12_OD[MX12_REG_THE_LOWEST_LIMIT_VOLTAGE       ].Address=12;   MX12_OD[MX12_REG_THE_LOWEST_LIMIT_VOLTAGE       ].Bytes=1;
+    MX12_OD[MX12_REG_THE_HIGHEST_LIMIT_VOLTAGE      ].Address=13;   MX12_OD[MX12_REG_THE_HIGHEST_LIMIT_VOLTAGE      ].Bytes=1;
+    MX12_OD[MX12_REG_MAX_TORQUE                     ].Address=14;   MX12_OD[MX12_REG_MAX_TORQUE                     ].Bytes=2;
+    MX12_OD[MX12_REG_STATUS_RETURN_LEVEL            ].Address=16;   MX12_OD[MX12_REG_STATUS_RETURN_LEVEL            ].Bytes=1;
+    MX12_OD[MX12_REG_ALARM_LED                      ].Address=17;   MX12_OD[MX12_REG_ALARM_LED                      ].Bytes=1;
+    MX12_OD[MX12_REG_ALARM_SHUTDOWN                 ].Address=18;   MX12_OD[MX12_REG_ALARM_SHUTDOWN                 ].Bytes=1;
+    MX12_OD[MX12_REG_MULTI_TURN_OFFSET              ].Address=20;   MX12_OD[MX12_REG_MULTI_TURN_OFFSET              ].Bytes=2;
+    MX12_OD[MX12_REG_RESOLUTION_DIVIDER             ].Address=22;   MX12_OD[MX12_REG_RESOLUTION_DIVIDER             ].Bytes=1;
+    MX12_OD[MX12_REG_TORQUE_ENABLE                  ].Address=24;   MX12_OD[MX12_REG_TORQUE_ENABLE                  ].Bytes=1;
+    MX12_OD[MX12_REG_LED                            ].Address=25;   MX12_OD[MX12_REG_LED                            ].Bytes=1;
+    MX12_OD[MX12_REG_D_GAIN                         ].Address=26;   MX12_OD[MX12_REG_D_GAIN                         ].Bytes=1;
+    MX12_OD[MX12_REG_I_GAIN                         ].Address=27;   MX12_OD[MX12_REG_I_GAIN                         ].Bytes=1;
+    MX12_OD[MX12_REG_P_GAIN                         ].Address=28;   MX12_OD[MX12_REG_P_GAIN                         ].Bytes=1;
+    MX12_OD[MX12_REG_GOAL_POSITION                  ].Address=30;   MX12_OD[MX12_REG_GOAL_POSITION                  ].Bytes=2;
+    MX12_OD[MX12_REG_MOVING_SPEED                   ].Address=32;   MX12_OD[MX12_REG_MOVING_SPEED                   ].Bytes=2;
+    MX12_OD[MX12_REG_TORQUE_LIMIT                   ].Address=34;   MX12_OD[MX12_REG_TORQUE_LIMIT                   ].Bytes=2;
+    MX12_OD[MX12_REG_PRESENT_POSITION               ].Address=36;   MX12_OD[MX12_REG_PRESENT_POSITION               ].Bytes=2;
+    MX12_OD[MX12_REG_PRESENT_SPEED                  ].Address=38;   MX12_OD[MX12_REG_PRESENT_SPEED                  ].Bytes=2;
+    MX12_OD[MX12_REG_PRESENT_LOAD                   ].Address=40;   MX12_OD[MX12_REG_PRESENT_LOAD                   ].Bytes=2;
+    MX12_OD[MX12_REG_PRESENT_VOLTAGE                ].Address=42;   MX12_OD[MX12_REG_PRESENT_VOLTAGE                ].Bytes=1;
+    MX12_OD[MX12_REG_PRESENT_TEMPERATURE            ].Address=43;   MX12_OD[MX12_REG_PRESENT_TEMPERATURE            ].Bytes=1;
+    MX12_OD[MX12_REG_REGISTERED                     ].Address=44;   MX12_OD[MX12_REG_REGISTERED                     ].Bytes=1;
+    MX12_OD[MX12_REG_MOVING                         ].Address=46;   MX12_OD[MX12_REG_MOVING                         ].Bytes=1;
+    MX12_OD[MX12_REG_LOCK                           ].Address=47;   MX12_OD[MX12_REG_LOCK                           ].Bytes=1;
+    MX12_OD[MX12_REG_PUNCH                          ].Address=48;   MX12_OD[MX12_REG_PUNCH                          ].Bytes=2;
+    MX12_OD[MX12_REG_GOAL_ACCELERATION              ].Address=73;   MX12_OD[MX12_REG_GOAL_ACCELERATION              ].Bytes=1;
+    MX12OD_Object_initalized=true;
 }
 
 // Set the mode of the servo
 //  0 = Positional (0-300 degrees)
 //  1 = Rotational -1 to 1 speed
 int MX12::SetMode(int mode) {
-
-    if (mode == 1) { // set CR
-        SetCWLimit(0);
-        SetCCWLimit(0);
-        SetCRSpeed(0.0);
-    } else {
-        SetCWLimit(0);
-        SetCCWLimit(300);
-        SetCRSpeed(0.0);
-    }
+////
+////Need to implement this as per http://support.robotis.com/en/product/dynamixel/mx_series/mx-12w.htm#Actuator_Address_06
+////
+////Maybe also make a read mode?
+////
+//    if (mode == 1) { // set CR
+//        SetCWLimit(0);
+//        SetCCWLimit(0);
+//        SetCRSpeed(0.0);
+//    } else {
+//        SetCWLimit(0);
+//        SetCCWLimit(300);
+//        SetCRSpeed(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 MX12::SetGoal(float radians, int flags) {
+void MX12::Dump_OD_to_Serial(Serial &serialObject){
+    serialObject.printf("CW Angle Limit = %f Degrees\n",Get_CW_Angle_Limit());
+    serialObject.printf("CCW Angle Limit = %f Degrees\n",Get_CCW_Angle_Limit());
+    serialObject.printf("Max Torque = %f Percent\n",Get_Max_Torque());
+    serialObject.printf("Multi Turn Offset = %f Degrees\n",Get_Multi_Turn_Offset());
+    serialObject.printf("Goal Position = %f Degrees\n",Get_Goal_Position());
+    serialObject.printf("Moving Speed = %f Degrees/Second\n",Get_Moving_Speed());
+    serialObject.printf("Torque Limit = %f Percent\n",Get_Torque_Limit());
+    serialObject.printf("Punch = %f Percent\n",Get_Punch());
+    serialObject.printf("ID = %f int\n",Get_ID());
+    serialObject.printf("Baud Rate = %f Lookup\n",Get_Baud_Rate());
+    serialObject.printf("Return Delay Time = %f milliseconds\n",Get_Return_Delay_Time());
+    serialObject.printf("the Highest Limit Temperature = %f Celsius\n",Get_the_Highest_Limit_Temperature());
+    serialObject.printf("the Lowest Limit Voltage = %f Volts\n",Get_the_Lowest_Limit_Voltage());
+    serialObject.printf("the Highest Limit Voltage = %f Volts\n",Get_the_Highest_Limit_Voltage());
+    serialObject.printf("Status Return Level = %f int\n",Get_Status_Return_Level());
+    serialObject.printf("Alarm LED = %f Bitmap\n",Get_Alarm_LED());
+    serialObject.printf("Alarm Shutdown = %f Bitmap\n",Get_Alarm_Shutdown());
+    serialObject.printf("Resolution Divider = %f Ratio\n",Get_Resolution_Divider());
+    serialObject.printf("Torque Enable = %f bool\n",Get_Torque_Enable());
+    serialObject.printf("LED = %f Bitmap\n",Get_LED());
+    serialObject.printf("D Gain = %f Kd\n",Get_D_Gain());
+    serialObject.printf("I Gain = %f Ki\n",Get_I_Gain());
+    serialObject.printf("P Gain = %f Kp\n",Get_P_Gain());
+    serialObject.printf("Lock = %f bool\n",Get_Lock());
+    serialObject.printf("Goal Acceleration = %f Degrees/Second^2\n",Get_Goal_Acceleration());
+    serialObject.printf("Model Number = %f Bitmap\n",Get_Model_Number());
+    serialObject.printf("Present Position = %f Degrees\n",Get_Present_Position());
+    serialObject.printf("Present Speed = %f Degrees/Second\n",Get_Present_Speed());
+    serialObject.printf("Present Load = %f Percent\n",Get_Present_Load());
+    serialObject.printf("Version of Firmware = %f int\n",Get_Version_of_Firmware());
+    serialObject.printf("Present Voltage = %f Volts\n",Get_Present_Voltage());
+    serialObject.printf("Present Temperature = %f Celsius\n",Get_Present_Temperature());
+    serialObject.printf("Registered = %f bool\n",Get_Registered());
+    serialObject.printf("Moving = %f bool\n",Get_Moving());
+}
 
-    char reg_flag = 0;
+void MX12::Scan_For_Dynamixels(bool scan_all_baud_rates,int max_id)
+{
+    char ID_Num;
+    if(scan_all_baud_rates){
+        int baud_rate[12]={3000000,2500000,2250000,1000000,500000,400000,250000,200000,115200,57600,19200,9600};
+        char ii;
+        for(ii=0;ii<12;ii++){
+            ChangeUARTBaud(baud_rate[ii]);
+            for(ID_Num=0;ID_Num<=max_id;ID_Num++){
+                if(ping(ID_Num)){
+                    printf("Found a servo at ID=%#02x, Baud=%d\n",ID_Num,baud_rate[ii]);
+                }
+            }
+        }
+    }else{
+        for(ID_Num=0;ID_Num<=max_id;ID_Num++){
+            if(ping(ID_Num)){
+                printf("Found a servo at ID=%#02x\n",ID_Num);
+            }
+        }
+    }
+    
+}
+
+int MX12::SetBaud( int target_baud ) {
+    
+    short baud_int;
+    
+    if(target_baud<=1000000){
+        baud_int=2000000/target_baud-1;
+    }else if(target_baud==2250000){
+        baud_int=250;
+    }else if(target_baud==2500000){
+        baud_int=251;
+    }else if(target_baud==3000000){
+        baud_int=252;
+    }else{
+        printf("Error!  Invalid baud rate of %d!\n",target_baud);
+        return 0;
+    }
+    
+    #if MX12_DEBUG
+        printf("SetBaudRate to 0x%x\n",baud_int);
+    #endif
+    int ret=(write_short(MX12_REG_BAUD_RATE,baud_int));
+    
+    // Now change the UART serial rate to the same?
+    // But to allow changing several servos, allow user
+    // to do this at their liesure.
+    // ChangeUARTBaud( target_baud );
+    
+    return ret;
+}
+
+void MX12::ChangeUARTBaud( int target_baud ) {
+    mx12_out.baud(target_baud);
+    mx12_in.baud(target_baud);
+}
+
+short MX12::GetRawPosition(void) {
+    #if MX12_DEBUG
+        printf("\nGetRawPosition(%d)",_ID);
+    #endif
+    return read_short(MX12_REG_PRESENT_POSITION);
+}
+
+int MX12:: write_short(MX12ODIndex OD,short value)
+{
     char data[2];
 
-    // set the flag is only the register bit is set in the flag
-    if (flags == 0x2) {
-        reg_flag = 1;
-    }
-
-    // 1023 / 300 * degrees
-    short goal = (2048 * radians) / M_PI;
-    if (MX12_DEBUG) {
-        printf("SetGoal to 0x%x\n",goal);
-    }
-
-    data[0] = goal & 0xff; // bottom 8 bits
-    data[1] = goal >> 8;   // top 8 bits
+    data[0] = value & 0xff; // bottom 8 bits
+    data[1] = value >> 8;   // top 8 bits
 
     // write the packet, return the error code
-    int rVal = write(_ID, MX12_REG_GOAL_POSITION, 2, data, reg_flag);
-
-    if (flags == 1) {
-        // block until it comes to a halt
-        while (isMoving()) {}
-    }
-    return(rVal);
-}
-
-
-// Set continuous rotation speed from -1 to 1
-int MX12::SetCRSpeed(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);
+    int rVal = write(_ID, MX12_OD[OD].Address, MX12_OD[OD].Bytes, data);
 
     return(rVal);
 }
 
-
-int MX12::SetCWLimit (int degrees) {
-
-    char data[2];
+short MX12:: read_short(MX12ODIndex OD)
+{
     
-    // 1023 / 300 * degrees
-    short limit = (1023 * degrees) / 300;
-
-    if (MX12_DEBUG) {
-        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, MX12_REG_CW_LIMIT, 2, data));
-
-}
-
-int MX12::SetCCWLimit (int degrees) {
-
     char data[2];
-
-    // 1023 / 300 * degrees
-    short limit = (1023 * degrees) / 300;
+    data[1]=0;
 
-    if (MX12_DEBUG) {
-        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, MX12_REG_CCW_LIMIT, 2, data));
-}
-
-
-int MX12::SetID (int CurrentID, int NewID) {
-
-    char data[1];
-    data[0] = NewID;
-    if (MX12_DEBUG) {
-        printf("Setting ID from 0x%x to 0x%x\n",CurrentID,NewID);
-    }
-    return (write(CurrentID, MX12_REG_ID, 1, data));
-
-}
-
-
-// return 1 is the servo is still in flight
-int MX12::isMoving(void) {
-
-    char data[1];
-    read(_ID,MX12_REG_MOVING,1,data);
-    return(data[0]);
+    int ErrorCode = read(_ID, MX12_OD[OD].Address, MX12_OD[OD].Bytes, data);
+    short value = data[0] + (data[1] << 8);
+    
+    return (value);
 }
 
 
-void MX12::trigger(void) {
 
-    char TxBuf[16];
-    char sum = 0;
-
-    if (MX12_TRIGGER_DEBUG) {
-        printf("\nTriggered\n");
-    }
-
-    // Build the TxPacket first in RAM, then we'll send in one go
-    if (MX12_TRIGGER_DEBUG) {
-        printf("\nTrigger Packet\n  Header : 0xFF, 0xFF\n");
-    }
-
-    TxBuf[0] = 0xFF;
-    TxBuf[1] = 0xFF;
-
-    // ID - Broadcast
-    TxBuf[2] = 0xFE;
-    sum += TxBuf[2];
-
-    if (MX12_TRIGGER_DEBUG) {
-        printf("  ID : %d\n",TxBuf[2]);
-    }
-
-    // Length
-    TxBuf[3] = 0x02;
-    sum += TxBuf[3];
-    if (MX12_TRIGGER_DEBUG) {
-        printf("  Length %d\n",TxBuf[3]);
-    }
-
-    // Instruction - ACTION
-    TxBuf[4] = 0x04;
-    sum += TxBuf[4];
-    if (MX12_TRIGGER_DEBUG) {
-        printf("  Instruction 0x%X\n",TxBuf[5]);
-    }
-
-    // Checksum
-    TxBuf[5] = 0xFF - sum;
-    if (MX12_TRIGGER_DEBUG) {
-        printf("  Checksum 0x%X\n",TxBuf[5]);
-    }
-
-    // Transmit the packet in one burst with no pausing
-    for (int i = 0; i < 6 ; i++) {
-        _mx12.putc(TxBuf[i]);
-    }
-
-    // This is a broadcast packet, so there will be no reply
-
-    return;
-}
+         ////////////////////////////////////////////
+         //                                        //
+         //                                        //
+         //          PRIVATE FUNCTIONS             //
+         //                                        //
+         //                                        //
+         ////////////////////////////////////////////
 
 
-float MX12::GetPosition(void) {
 
-    if (MX12_DEBUG) {
-        printf("\nGetPosition(%d)",_ID);
-    }
-
-    char data[2];
-
-    int ErrorCode = read(_ID, MX12_REG_POSITION, 2, data);
-    short position = data[0] + (data[1] << 8);
-    float angle = (position * M_PI)/2048;
-
-    return (angle);
-}
-
-
-float MX12::GetTemp (void) {
-
-    if (MX12_DEBUG) {
-        printf("\nGetTemp(%d)",_ID);
-    }
-    char data[1];
-    int ErrorCode = read(_ID, MX12_REG_TEMP, 1, data);
-    float temp = data[0];
-    return(temp);
-}
-
-
-float MX12::GetVolts (void) {
-    if (MX12_DEBUG) {
-        printf("\nGetVolts(%d)",_ID);
-    }
-    char data[1];
-    int ErrorCode = read(_ID, MX12_REG_VOLTS, 1, data);
-    float volts = data[0]/10.0;
-    return(volts);
-}
 
 
 int MX12::read(int ID, int start, int bytes, char* data) {
-
+    
     char PacketLength = 0x4;
     char TxBuf[16];
     char sum = 0;
@@ -276,14 +232,14 @@
 
     Status[4] = 0xFE; // return code
 
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("\nread(%d,0x%x,%d,data)\n",ID,start,bytes);
-    }
+    #endif
 
     // Build the TxPacket first in RAM, then we'll send in one go
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("\nInstruction Packet\n  Header : 0xFF, 0xFF\n");
-    }
+    #endif
 
     TxBuf[0] = 0xff;
     TxBuf[1] = 0xff;
@@ -291,49 +247,61 @@
     // ID
     TxBuf[2] = ID;
     sum += TxBuf[2];
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("  ID : %d\n",TxBuf[2]);
-    }
+    #endif
 
     // Packet Length
     TxBuf[3] = PacketLength;    // Length = 4 ; 2 + 1 (start) = 1 (bytes)
     sum += TxBuf[3];            // Accululate the packet sum
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("  Length : 0x%x\n",TxBuf[3]);
-    }
+    #endif
 
     // Instruction - Read
     TxBuf[4] = 0x2;
     sum += TxBuf[4];
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("  Instruction : 0x%x\n",TxBuf[4]);
-    }
+    #endif
 
     // Start Address
     TxBuf[5] = start;
     sum += TxBuf[5];
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("  Start Address : 0x%x\n",TxBuf[5]);
-    }
+    #endif
 
     // Bytes to read
     TxBuf[6] = bytes;
     sum += TxBuf[6];
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("  No bytes : 0x%x\n",TxBuf[6]);
-    }
+    #endif
 
     // Checksum
     TxBuf[7] = 0xFF - sum;
-    if (MX12_READ_DEBUG) {
+    #if MX12_READ_DEBUG
         printf("  Checksum : 0x%x\n",TxBuf[7]);
+    #endif
+    
+    // Clear in input buffer first
+    while (mx12_in.readable()) {
+        mx12_in.getc();
+        printf("Purging one character (read).\n");
     }
-
+    
     // Transmit the packet in one burst with no pausing
     for (int i = 0; i<8 ; i++) {
-        _mx12.putc(TxBuf[i]);
+        mx12_out.putc(TxBuf[i]);
     }
-
+    
+    // Read
+    for (int i = 0; i<8 ; i++) {
+        profileOut=i%2;
+        mx12_in.getc();
+    }
+    
     // Wait for the bytes to be transmitted
     //wait (0.00002);
 
@@ -341,31 +309,43 @@
     if (_ID != 0xFE) {
 
         // Receive the Status packet 6+ number of bytes read
-        if (MX12_READ_DEBUG) {
+        #if MX12_READ_DEBUG
             printf("  Reading Byte:");
-        }
+        #endif
+        #if MX12_DEBUG
         Timer serial_timeout;
+        serial_timeout.start ();
+        #endif
+        
         for (int i=0; i<(6+bytes) ; i++) {
+            #if MX12_DEBUG
             serial_timeout.reset ();
-            while (!_mx12.readable ())
+            while (!mx12_in.readable ())
             {
                 //Just loop here until you either get a character, or we timeout
-                if (serial_timeout.read_ms () > MAX_DELAY_BETWEEN_CHARCTERS_IN_MS)
+                if (serial_timeout.read_us () > MAX_DELAY_BETWEEN_CHARCTERS_IN_US)
                 {
                     //If we timeout, quit in a panic!
-                    if (MX12_READ_DEBUG) {
-                        printf("\nTimeout waiting for serial response!\nReceived %d characters.\n",i);
-                    }
+                    printf("\nTimeout waiting for serial response!\nReceived %d characters.\n",i);
                     return(0x00);
                 }
             }
-            Status[i] = _mx12.getc();
-            if (MX12_READ_DEBUG) {
+            #endif
+            Status[i] = mx12_in.getc();
+            #if MX12_READ_DEBUG
                 printf("%d",i);
-            }
+            #endif
         }
-        if (MX12_READ_DEBUG) {
+        
+        #if MX12_READ_DEBUG
             printf("\n");
+        #endif
+        if(Status[0]!=0xFF || Status[1]!=0xFF)
+        {
+            printf("Unexpected header in serial response!\n");
+            printf("  Header : 0x%x\n",Status[0]);
+            printf("  Header : 0x%x\n",Status[1]);
+            return 0;
         }
 
         // Copy the data from Status into data for return
@@ -373,7 +353,7 @@
             data[i] = Status[5+i];
         }
 
-        if (MX12_READ_DEBUG) {
+        #if MX12_READ_DEBUG
             printf("\nStatus Packet\n");
             printf("  Header : 0x%x\n",Status[0]);
             printf("  Header : 0x%x\n",Status[1]);
@@ -386,7 +366,7 @@
             }
 
             printf("  Checksum : 0x%x\n",Status[5+(Status[3]-2)]);
-        }
+        #endif
 
     } // if (ID!=0xFE)
 
@@ -394,84 +374,90 @@
 }
 
 
-int MX12:: write(int ID, int start, int bytes, char* data, int flag) {
+
+
+int MX12:: write(int ID, int start, int bytes, char* data) {
 // 0xff, 0xff, ID, Length, Intruction(write), Address, Param(s), Checksum
 
     char TxBuf[16];
     char sum = 0;
     char Status[6];
 
-    if (MX12_WRITE_DEBUG) {
+    #if MX12_WRITE_DEBUG
         printf("\nwrite(%d,0x%x,%d,data,%d)\n",ID,start,bytes,flag);
-    }
+    #endif
 
     // Build the TxPacket first in RAM, then we'll send in one go
-    if (MX12_WRITE_DEBUG) {
+    #if MX12_WRITE_DEBUG
         printf("\nInstruction Packet\n  Header : 0xFF, 0xFF\n");
-    }
+    #endif
 
-    TxBuf[0] = 0xff;
-    TxBuf[1] = 0xff;
+    TxBuf[0] = MX12_INSTRUCTION_HEADER;
+    TxBuf[1] = MX12_INSTRUCTION_HEADER;
 
     // ID
     TxBuf[2] = ID;
     sum += TxBuf[2];
 
-    if (MX12_WRITE_DEBUG) {
+    #if MX12_WRITE_DEBUG
         printf("  ID : %d\n",TxBuf[2]);
-    }
+    #endif
 
     // packet Length
     TxBuf[3] = 3+bytes;
     sum += TxBuf[3];
 
-    if (MX12_WRITE_DEBUG) {
+    #if MX12_WRITE_DEBUG
         printf("  Length : %d\n",TxBuf[3]);
-    }
+    #endif
 
     // Instruction
-    if (flag == 1) {
-        TxBuf[4]=0x04;
-        sum += TxBuf[4];
-    } else {
+//    if (flag == 1) {
+//        TxBuf[4]=0x04;
+//    } else {
         TxBuf[4]=0x03;
-        sum += TxBuf[4];
-    }
+//    }
+    sum += TxBuf[4];
 
-    if (MX12_WRITE_DEBUG) {
+    #if MX12_WRITE_DEBUG
         printf("  Instruction : 0x%x\n",TxBuf[4]);
-    }
+    #endif
 
     // Start Address
     TxBuf[5] = start;
     sum += TxBuf[5];
-    if (MX12_WRITE_DEBUG) {
+    #if MX12_WRITE_DEBUG
         printf("  Start : 0x%x\n",TxBuf[5]);
-    }
+    #endif
 
     // data
     for (char i=0; i<bytes ; i++) {
         TxBuf[6+i] = data[i];
         sum += TxBuf[6+i];
-        if (MX12_WRITE_DEBUG) {
+        #if MX12_WRITE_DEBUG
             printf("  Data : 0x%x\n",TxBuf[6+i]);
-        }
+        #endif
     }
 
     // checksum
     TxBuf[6+bytes] = 0xFF - sum;
-    if (MX12_WRITE_DEBUG) {
+    #if MX12_WRITE_DEBUG
         printf("  Checksum : 0x%x\n",TxBuf[6+bytes]);
+    #endif
+    
+    // Clear in input buffer first
+    while (mx12_in.readable()) {
+        mx12_in.getc();
+        printf("Purging one character (write).\n");
     }
 
     // Transmit the packet in one burst with no pausing
     for (int i = 0; i < (7 + bytes) ; i++) {
-        _mx12.putc(TxBuf[i]);
+        mx12_out.putc(TxBuf[i]);
+        mx12_in.getc();
+        //printf("Echo: 0x%02x\n",mx12_in.getc());
     }
 
-    // Wait for data to transmit
-    wait (0.00002);
-
     // make sure we have a valid return
     Status[4]=0x00;
 
@@ -479,36 +465,316 @@
     if (_ID!=0xFE) {
 
         // response is always 6 bytes
-        // 0xFF, 0xFF, ID, Length Error, Param(s) Checksum
+        // 0xFF, 0xFF, ID, Length, Error, Checksum
+        #if MX12_DEBUG
         Timer serial_timeout;
+        serial_timeout.start ();
+        #endif
+        
         for (int i=0; i < 6 ; i++) {
+            #if MX12_DEBUG
             serial_timeout.reset ();
-            while (!_mx12.readable ())
+            while (!mx12_in.readable ())
             {
                 //Just loop here until you either get a character, or we timeout
-                if (serial_timeout.read_ms () > MAX_DELAY_BETWEEN_CHARCTERS_IN_MS)
+                if (serial_timeout.read_us () > MAX_DELAY_BETWEEN_CHARCTERS_IN_US)
                 {
                     //If we timeout, quit in a panic!
-                    if (MX12_WRITE_DEBUG) {
-                        printf("\nTimeout waiting for serial response!\nReceived %d characters.\n",i);
-                    }
+                    printf("\nTimeout waiting for serial response!\nReceived %d characters.\n",i);
                     return(0x00);
                 }
             }
-            Status[i] = _mx12.getc();
+            #endif
+            Status[i] = mx12_in.getc();
         }
-
+        
         // Build the TxPacket first in RAM, then we'll send in one go
-        if (MX12_WRITE_DEBUG) {
+        #if MX12_WRITE_DEBUG
             printf("\nStatus Packet\n  Header : 0x%X, 0x%X\n",Status[0],Status[1]);
             printf("  ID : %d\n",Status[2]);
             printf("  Length : %d\n",Status[3]);
             printf("  Error : 0x%x\n",Status[4]);
             printf("  Checksum : 0x%x\n",Status[5]);
-        }
+        #endif
 
 
     }
 
     return(Status[4]); // return error code
 }
+
+void MX12::coordinated_move(char id0, short pos0, short vel0, char id1, short pos1, short vel1)
+{
+    char NumDevices   = 0x2;
+    char DataLength   = 0x4;//!< Hardcoded for now, 2 bytes for pos, 2 bytes for vel
+    char PacketLength = 0x4+NumDevices*(DataLength+0x1);
+    char StartAddress = MX12_OD[MX12_REG_GOAL_POSITION].Address;
+    char TxBuf[20];
+    char sum = 0;
+    char ii=0;
+    char jj=0;
+    char offset=0;
+    
+    // Hardcoded for now, 2 devices
+    char ID_Num[NumDevices];
+    ID_Num[0]=id0;
+    ID_Num[1]=id1;
+    
+    // Hardcoded for now, 2 bytes for pos, 2 bytes for vel, 2 devices
+    char data[NumDevices][DataLength];
+    // A faster/better way to do this would be with a fancy memcpy from 
+    // short to char or maybe a union or just raw pointers...
+    data[0][0] = pos0 & 0xff; // bottom 8 bits
+    data[0][1] = pos0 >> 8;   // top 8 bits
+    data[0][2] = vel0 & 0xff; // bottom 8 bits
+    data[0][3] = vel0 >> 8;   // top 8 bits
+    data[1][0] = pos1 & 0xff; // bottom 8 bits
+    data[1][1] = pos1 >> 8;   // top 8 bits
+    data[1][2] = vel1 & 0xff; // bottom 8 bits
+    data[1][3] = vel1 >> 8;   // top 8 bits
+    
+    // Build the TxPacket first in RAM, then we'll send in one go
+    #if MX12_WRITE_DEBUG
+        printf("\nInstruction Packet\n  Header : 0xFF, 0xFF\n");
+    #endif
+
+    TxBuf[0] = 0xff;
+    TxBuf[1] = 0xff;
+
+    // ID
+    TxBuf[2] = 0xfe;
+    sum += TxBuf[2];
+    #if MX12_WRITE_DEBUG
+        printf("  ID : %d\n",TxBuf[2]);
+    #endif
+
+    // Packet Length
+    TxBuf[3] = PacketLength;    // Length = 4 ; 2 + 1 (start) = 1 (bytes)
+    sum += TxBuf[3];            // Accululate the packet sum
+    #if MX12_WRITE_DEBUG
+        printf("  Length : 0x%x\n",TxBuf[3]);
+    #endif
+
+    // Instruction - Sync Write
+    TxBuf[4] = 0x83;
+    sum += TxBuf[4];
+    #if MX12_WRITE_DEBUG
+        printf("  Instruction : 0x%x\n",TxBuf[4]);
+    #endif
+
+    // Start Address
+    TxBuf[5] = StartAddress;
+    sum += TxBuf[5];
+    #if MX12_WRITE_DEBUG
+        printf("  Start Address : 0x%x\n",TxBuf[5]);
+    #endif
+
+    // Bytes to write to each device
+    TxBuf[6] = DataLength;
+    sum += TxBuf[6];
+    #if MX12_WRITE_DEBUG
+        printf("  No bytes : 0x%x\n",TxBuf[6]);
+    #endif
+
+    // The data itself
+    for(ii=0;ii<NumDevices;ii++){
+        // Store this offset in a variable since we use it a lot.
+        // The 7 comes from the fact that the last write was at 6,
+        // so this one should start at 7.  The 0x1 is for the ID.
+        offset=7+ii*(DataLength+0x1);
+        // Write the ID of the device
+        TxBuf[offset] = ID_Num[ii];
+        sum += TxBuf[offset];
+        
+        #if MX12_WRITE_DEBUG
+            printf("  ID #%d : 0x%x\n",ii,TxBuf[offset]);
+        #endif
+    
+        // Write each of the bytes of the data
+        for(jj=0;jj<DataLength;ii++){
+            TxBuf[offset+jj] = data[ii][jj];
+            sum += TxBuf[offset+jj];
+            #if MX12_WRITE_DEBUG
+                printf("  Data #%d : 0x%x\n",jj,TxBuf[offset+jj]);
+            #endif
+        }
+    }
+    
+
+    // Checksum
+    offset=7+NumDevices*(DataLength+0x1);
+    TxBuf[offset] = 0xFF - sum;
+    #if MX12_WRITE_DEBUG
+        printf("  Checksum : 0x%x\n",TxBuf[offset]);
+    #endif
+    
+    // Clear in input buffer first
+    while (mx12_in.readable()) {
+        mx12_in.getc();
+        printf("Purging one character (read).\n");
+    }
+    
+    // Transmit the packet in one burst with no pausing
+    offset=7+NumDevices*(DataLength+0x1)+0x1; // one more for the checksum
+    for (ii = 0; ii<offset ; ii++) {
+        mx12_out.putc(TxBuf[ii]);
+    }
+    
+    // Read
+    for (ii = 0; ii<offset ; ii++) {
+        profileOut=ii%2;
+        mx12_in.getc();
+    }
+}
+
+void MX12::trigger(void) {
+
+    char TxBuf[6];
+    char sum = 0;
+
+    #if MX12_TRIGGER_DEBUG
+        printf("\nTriggered\n");
+    #endif
+
+    // Build the TxPacket first in RAM, then we'll send in one go
+    #if MX12_TRIGGER_DEBUG
+        printf("\nTrigger Packet\n  Header : 0xFF, 0xFF\n");
+    #endif
+
+    TxBuf[0] = 0xFF;
+    TxBuf[1] = 0xFF;
+
+    // ID - Broadcast
+    TxBuf[2] = 0xFE;
+    sum += TxBuf[2];
+
+    #if MX12_TRIGGER_DEBUG
+        printf("  ID : %d\n",TxBuf[2]);
+    #endif
+
+    // Length
+    TxBuf[3] = 0x02;
+    sum += TxBuf[3];
+    #if MX12_TRIGGER_DEBUG
+        printf("  Length %d\n",TxBuf[3]);
+    #endif
+
+    // Instruction - ACTION
+    TxBuf[4] = 0x05;
+    sum += TxBuf[4];
+    #if MX12_TRIGGER_DEBUG
+        printf("  Instruction 0x%X\n",TxBuf[5]);
+    #endif
+
+    // Checksum
+    TxBuf[5] = 0xFF - sum;
+    #if MX12_TRIGGER_DEBUG
+        printf("  Checksum 0x%X\n",TxBuf[5]);
+    #endif
+
+    // Transmit the packet in one burst with no pausing
+    for (int i = 0; i < 6 ; i++) {
+        mx12_out.putc(TxBuf[i]);
+    }
+    
+    // Read
+    for (int i = 0; i < 6 ; i++) {
+        profileOut=i%2;
+        mx12_in.getc();
+    }
+
+    // This is a broadcast packet, so there will be no reply
+
+    return;
+}
+
+bool MX12::ping(char ID_Num) {
+    if(ID_Num==0xFF){
+        // Default to _ID
+        ID_Num=_ID;
+    }
+    
+    char TxBuf[6];
+    char sum = 0;
+    char Status[6];
+
+    #if MX12_TRIGGER_DEBUG
+        printf("\nTriggered\n");
+    #endif
+
+    // Build the TxPacket first in RAM, then we'll send in one go
+    #if MX12_TRIGGER_DEBUG
+        printf("\nTrigger Packet\n  Header : 0xFF, 0xFF\n");
+    #endif
+
+    TxBuf[0] = 0xFF;
+    TxBuf[1] = 0xFF;
+
+    // ID
+    TxBuf[2] = ID_Num;
+    sum += TxBuf[2];
+
+    #if MX12_TRIGGER_DEBUG
+        printf("  ID : %d\n",TxBuf[2]);
+    #endif
+
+    // Length
+    TxBuf[3] = 0x02;
+    sum += TxBuf[3];
+    #if MX12_TRIGGER_DEBUG
+        printf("  Length %d\n",TxBuf[3]);
+    #endif
+
+    // Instruction - PING
+    TxBuf[4] = 0x01;
+    sum += TxBuf[4];
+    #if MX12_TRIGGER_DEBUG
+        printf("  Instruction 0x%X\n",TxBuf[5]);
+    #endif
+
+    // Checksum
+    TxBuf[5] = 0xFF - sum;
+    #if MX12_TRIGGER_DEBUG
+        printf("  Checksum 0x%X\n",TxBuf[5]);
+    #endif
+
+    // Transmit the packet in one burst with no pausing
+    for (int i = 0; i < 6 ; i++) {
+        mx12_out.putc(TxBuf[i]);
+    }
+    
+    // Read
+    for (int i = 0; i < 6 ; i++) {
+        profileOut=i%2;
+        mx12_in.getc();
+    }
+
+    
+    // response is always 6 bytes
+    // 0xFF, 0xFF, ID, Length, Error, Checksum
+    Timer serial_timeout;
+    serial_timeout.start ();
+    
+    for (int i=0; i < 6 ; i++) {
+        serial_timeout.reset ();
+        while (!mx12_in.readable ())
+        {
+            //Just loop here until you either get a character, or we timeout
+            if (serial_timeout.read_us () > MAX_DELAY_BETWEEN_CHARCTERS_IN_US)
+            {
+                //If we timeout, quit in a panic!
+                //printf("\nTimeout waiting for serial response!\nReceived %d characters.\n",i);
+                return false;
+            }
+        }
+        Status[i] = mx12_in.getc();
+    }
+    
+    printf("\nStatus Packet\n  Header : 0x%X, 0x%X\n",Status[0],Status[1]);
+    printf("  ID : %d\n",Status[2]);
+    printf("  Length : %d\n",Status[3]);
+    printf("  Error : 0x%x\n",Status[4]);
+    printf("  Checksum : 0x%x\n",Status[5]);
+
+    return true;
+}
\ No newline at end of file